Cela aide-t-il GC à annuler les variables locales en Java

J’étais “obligé” d’append myLocalVar = null; déclaration en clause finale juste avant de quitter la méthode. La raison est d’aider GC. On m’a dit que j’aurais des SMS pendant la nuit quand le serveur planterait la prochaine fois, alors je ferais mieux de le faire :-).

Je pense que cela est inutile, car myLocalVar est orienté vers la méthode et sera «perdu» dès que la méthode sera terminée. Une annulation supplémentaire ne fait que polluer le code, mais est sans danger sinon.

Ma question est la suivante: d’où vient ce mythe pour aider GC? (J’ai été référé à “Java memory books”) Connaissez-vous un article des “autorités” qui l’explique plus en détail? Y a-t-il une possibilité que ce ne soit pas un mythe, mais que cela aide vraiment? Si c’est le cas, comment? Peut-on nuire aux variables locales?

Pour clarifier, la méthode ressemble à ceci:

 void method() { MyClass myLocalVar = null; try { myLocalVar = get reference to object; ... do more here ... } finally { if (myLocalVar != null) { myLocalVar.close(); // it is resource which we should close } myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT } } 

Il y avait un vieux document de Sun, Java Platform Performance (un lien malheureusement rompu, et je n’ai pas pu en trouver un nouveau), qui décrivait une situation dans laquelle l’annulation d’une variable locale hors de scope avait un effet sur le GC.

Cependant, le document faisait référence à une très ancienne version de java. Comme mentionné dans cette question (qui contient également un résumé du problème décrit dans l’article), cela n’affecte plus les implémentations JVM actuelles.

Le Java GC est supposé être “sain” mais n’est pas nécessairement immédiatement “complet”. En d’autres termes, il est conçu pour ne jamais éliminer les objects encore accessibles par au moins un chemin (et donc provoquer une référence en suspens). Ce n’est pas nécessairement immédiatement complet car cela peut prendre du temps jusqu’à ce qu’il supprime tout ce qui peut être supprimé.

Je pense que la plupart des mythes du GC proviennent d’une mauvaise compréhension de ce concept. Beaucoup de gens gardent trop de variables d’instance en vie, ce qui pose des problèmes, mais ce n’est évidemment pas le problème ici.

D’autres personnes placent les variables locales dans une variable d’instance (par exemple en la passant à la fonction), puis pensent que l’annulation de la variable locale élimine d’une manière ou d’une autre la variable, ce qui est bien sûr faux.

Enfin, il y a des gens qui surpassent le GC et pensent qu’il ferait un arrêt fonctionnel pour eux (par exemple, fermer les connexions lorsque la variable est supprimée), ce qui n’est bien sûr pas le cas. Je pense que la source de cette ligne est la “Je suis vraiment vraiment fait avec mais je ne suis pas sûr de savoir comment faire cela”.

Donc oui, vous avez raison, c’est inutile.

Pas dans ce cas. myLocalVar est hors de scope dès que la fonction retourne, ainsi, définir la référence à null ne fait absolument rien.

C’est un mythe qui remonte à l’époque où Java est apparu pour la première fois et où les gars de C ++ ne faisaient pas confiance au gc.

Le gc sait ce qu’il fait. annuler la var ne nuira à rien, mais cela n’aidera pas vraiment non plus. Jeff a publié un billet assez drôle juste l’autre jour.

À ma connaissance, annuler une variable immédiatement avant qu’elle ne quitte la scope ne fait aucune différence pour le ramasse-miettes.

Bien sûr, il y a des cas où cela aide effectivement. Par exemple, lorsque var n’est pas une variable locale mais un membre ou un membre statique. La destruction de la référence peut alors rendre l’object inaccessible et donc éligible pour la collecte.

Un autre cas où cela pourrait aider même avec des variables locales si une fonction alloue beaucoup de mémoire temporaire pour initialiser certaines données pour un traitement ultérieur et peut rejeter toutes les références à la mémoire temporaire avant de commencer le traitement:

 SomeResult SomeFunction(SomeClass param) { TempData big = new TempData(param); IntermediateResult intermediate = big.GetIntermediateResult(); big = null; // allow GC to reclaim the memory before returning from the function intermediate.FurtherProcessing(); return intermediate.EvenMoreProcessing(); } 

Vous avez raison. Il est inutile d’annuler une variable qui tombera immédiatement hors de la scope et qui ne fait aucune différence pour GC. Tout ce qu’il fait est d’encombrer le code. Dans Effective Java 2nd Edition , l’auteur recommande d’éviter toute annulation inutile des variables locales. Voir Effective Java, 2nd Edition, Élément 6: Éliminez les références d’object obsolètes, pour un rendu complet.

Vous pouvez également voir ceci dans l’article Créer et détruire des objects Java , dans InformIT. Lisez l’article en entier pour trouver l’endroit où Joshua Bloch est d’accord avec vous.

Lorsqu’une variable locale est hors de scope, elle est exactement la même que si vous annuliez la référence.

EDIT: Ajouter un lien vers Effective Java 2nd Edition sur le site Web de Sun

La suppression des variables locales peut en effet aider dans certains cas extrêmes. Cela ne s’applique pas à la situation dans la question originale, mais est éducatif de toute façon … Considérons ce programme:

 public class Main { public static void main(Ssortingng[] args) { { Main local = new Main(); // inner = null; } while (true) { // long running method } } } 

Si inner = null; est mis en commentaire, l’object dans local variable local ne peut pas être récupéré pendant une boucle while. La raison en est que Java Virtual Machine ne connaît pas de telles étendues. Tout ce qu’il a c’est:

 D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main public class Main extends java.lang.Object minor version: 0 major version: 50 Constant pool: const #1 = Method #4.#11; // java/lang/Object."":()V const #2 = class #12; // Main const #3 = Method #2.#11; // Main."":()V const #4 = class #13; // java/lang/Object const #5 = Asciz ; const #6 = Asciz ()V; const #7 = Asciz Code; const #8 = Asciz main; const #9 = Asciz ([Ljava/lang/Ssortingng;)V; const #10 = Asciz StackMapTable; const #11 = NameAndType #5:#6;// "":()V const #12 = Asciz Main; const #13 = Asciz java/lang/Object; { public Main(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static void main(java.lang.Ssortingng[]); Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class Main 3: dup 4: invokespecial #3; //Method "":()V 7: astore_1 8: goto 8 StackMapTable: number_of_ensortinges = 1 frame_type = 8 /* same */ } 

Il n’y a pas d’informations sur la scope de la variable locale. Donc, du sharepoint vue de la JVM, le programme ci-dessus équivaut à:

 public class Main { public Main() { } public static void main(Ssortingng args[]) { Main main1 = new Main(); do ; while(true); } } 

(Généré par le décompilateur JAD)

Conclusion: il est logique d’annuler les variables locales dans des cas très particuliers comme celui-ci. Mais si la méthode va bientôt se terminer (comme dans ma question initiale), cela n’aide pas.

Cela a été inspiré par le commentaire de Zdenek Tronicek sur la liste de diffusion java-cz (en langue tchèque, désolé)

Comme vous le soulignez à juste titre, annuler dans ce cas est totalement inutile.

De retour sur JDK1.3, j’ai effectivement eu un cas avec un graphe d’object extrêmement grand qui contenait également beaucoup de références circulaires dans le graphique. Effacer certaines des références a réellement amélioré les temps de GC.

Je ne suis pas sûr que cela s’applique à une machine virtuelle moderne. Les éboueurs sont de plus en plus intelligents.

Un autre facteur possible dans ce mythe est que cela peut faire une différence en annulant une variable locale si vous en avez fini avec la fin de la méthode. Cela permettrait au GC de collecter cet object avant la fin de la méthode, ce qui pourrait être utile.

Quelqu’un pourrait avoir reçu ce conseil à un moment donné et l’a mal compris comme un besoin de toujours annuler les variables locales.

Il n’y a que deux cas où j’ai trouvé que définir une variable sur null était utile:

  • Dans les tests unitaires qui créent un object volumineux dans un champ. Le testeur d’unité peut conserver l’object de test et les objects que vous avez créés pour la durée de vie de tous les tests. Cela peut provoquer le manque de mémoire du testeur. Dans ce cas, il est souvent préférable d’utiliser des variables locales au lieu de champs, mais si un champ est requirejs, il peut être effacé dans le répertoire.
  • Les références circulaires peuvent être nettoyées par le GC, mais pas avec une simple collection incrémentielle. Cela peut signifier que les objects avec des références circulaires prennent plus de temps à effacer et peuvent vivre beaucoup plus longtemps qu’ils ne le feraient autrement. En général, cela n’a pas d’importance, mais si vous essayez de réduire votre temps GC complet, cela peut aider à casser des références circulaires.

Je ne connais pas les détails techniques, mais pour autant que je puisse m’en souvenir, la variable n’est rien de plus que la référence du frame de stack actuel et tant que cette référence n’est pas supprimée, l’object ne peut pas être récupéré. Maintenant, mais en le définissant explicitement sur null, vous vous êtes assuré que la référence a disparu. Si vous ne le faites pas, vous laissez fondamentalement la machine virtuelle décider du moment où cette référence est effacée, ce qui peut être le cas ou non (contrairement à C ++, si l’object est situé sur la stack et DOIT être détruit). Ce peut être lorsque le cadre de la stack est remplacé par le suivant. Je ne suis pas sûr qu’il y ait réellement une VM qui le fasse.

Réponse courte cependant, c’est inutile, la marque et le balayage finiront par l’obtenir. C’est au plus une question de temps.

Dans certaines circonstances, cela peut être utile pour les variables nulles (généralement les variables d’instance ou de classe). Mais annuler une variable locale immédiatement avant la fin de la méthode ne fait absolument rien.

Lorsque vous définissez une variable sur null , vous supprimez simplement cette référence à l’object réel. Mais quand une variable locale est hors de scope, la référence est supprimée de toute façon; par conséquent, définir la valeur null comme la dernière ligne de la méthode est simplement redondant.

Si votre classe rest longtemps dans le coin, l’annulation des objects référencés permettra leur collecte.

Ce n’est presque jamais un problème, la plupart du temps, la nullité des objects n’est pas utile.

Quand vous pensez à l’allocation et à la libération d’object, faites attention aux choses que le “Système” gère: Sujets actifs, fenêtres qui n’ont pas été éliminées () d, et une ou deux autres choses mais je ne me souviens pas bien à présent.

Chaque object de votre système “se bloque” sur ces points de assembly dans un arbre géant à l’envers. Si vous coupez une twig sans ces «racines», la twig entière tombe au sol et est collectée par la tondeuse à gazon.

La plupart des classes ont besoin de toutes leurs variables membres pour l’ensemble de leur cycle de vie – et lorsque leur vie est terminée, toute leur twig est réduite, y compris tous leurs membres. par conséquent – pas besoin de null.

(en passant, ces sortingms sont assez efficaces, même plus que C ++, car ils ne nécessitent pas de toucher chaque object à mesure qu’il est libéré)

Si vous n’avez plus besoin d’objects volumineux dans votre environnement local, vous pouvez donner un indice à la machine virtuelle Java et définir la référence NULL.

 public void foobar() { List dataList = new ArrayList(); // heavy computation here where objects are added to dataList // and the list grows, maybe you will sort the list and // you just need the first element... SomeObject smallest = dataList.get(0); // more heavy computation will follow, where dataList is never used again // so give the JVM a hint to drop it on its on discretion dataList = null; // ok, do your stuff other heavy stuff here... maybe you even need the // memory that was occupied by dataList before... // end of game and the JVM will take care of everything, no hints needed } 

Mais cela n’a pas de sens avant le retour, car cela est fait automatiquement par la JVM. Je suis donc d’accord avec tous les messages avant.

Non seulement la nullité d’une variable locale comme celle qui n’a pas de sens en termes de CPG peut entraîner un chargement inutile de la variable dans un registre afin de le supprimer, ce qui aggrave la situation. Imaginez s’il y a 1000 lignes de code entre la dernière lecture ou écriture sur myLocalVar, et ensuite vous le référencez juste pour annuler la référence. La valeur a disparu depuis longtemps du registre, mais vous devez la recharger en mémoire pour pouvoir l’utiliser.