L'affectation d'une référence d'objet inutilisée à null
dans Java améliore-t-elle le processus de récupération de place de manière mesurable?
Mon expérience avec Java (et C #) m'a appris qu'il est souvent contre-intuitif d'essayer de déjouer la machine virtuelle ou le compilateur JIT, mais j'ai vu des collègues utiliser cette méthode et je suis curieux de savoir si c'est une bonne pratique à prendre ou une de ces superstitions de programmation vaudou?
En règle générale, non.
Mais comme toutes choses: cela dépend. Le GC en Java ces jours-ci est TRÈS bon et tout devrait être nettoyé très peu de temps après qu'il ne soit plus accessible. C'est juste après avoir quitté une méthode pour les variables locales, et quand une instance de classe est n'est plus référencé pour les champs.
Vous devez uniquement annuler explicitement si vous savez qu'il resterait référencé autrement. Par exemple, un tableau qui est conservé. Vous souhaiterez peut-être annuler les éléments individuels du tableau lorsqu'ils ne sont plus nécessaires.
Par exemple, ce code de ArrayList:
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
De plus, l'annulation explicite d'un objet n'entraînera pas la collecte d'un objet plus tôt que s'il sortait naturellement du domaine tant qu'aucune référence ne subsiste.
Tous les deux:
void foo() {
Object o = new Object();
/// do stuff with o
}
et:
void foo() {
Object o = new Object();
/// do stuff with o
o = null;
}
Sont fonctionnellement équivalents.
Le bon article est aujourd'hui horreur de codage .
La façon dont GC fonctionne est en recherchant des objets qui n'ont pas de pointeurs vers eux, la zone de leur recherche est tas/pile et tous les autres espaces dont ils disposent. Donc, si vous définissez une variable sur null, l'objet réel n'est désormais pointé par personne et pourrait donc être GC'd.
Mais comme le GC peut ne pas fonctionner à cet instant précis, vous ne vous achetez peut-être rien. Mais si votre méthode est assez longue (en termes de temps d'exécution), cela pourrait valoir la peine car vous augmenterez vos chances de récupérer cet objet par GC.
Le problème peut également être compliqué avec les optimisations de code, si vous n'utilisez jamais la variable après l'avoir définie sur null, ce serait une optimisation sûre pour supprimer la ligne qui définit la valeur sur null (une instruction de moins à exécuter). Donc, vous n'obtiendrez peut-être aucune amélioration.
Donc, en résumé, oui, cela peut aider, mais ce ne sera pas déterministe .
D'après mon expérience, le plus souvent, les gens annulent les références par paranoïa, pas par nécessité. Voici un guide rapide:
Si l'objet A fait référence à l'objet B et vous n'avez plus besoin de cette référence et l'objet A n'est pas éligible pour le garbage collection, vous devez alors explicitement annuler le champ. Il n'est pas nécessaire d'annuler un champ si l'objet englobant est de toute façon récupéré. La suppression des champs dans une méthode dispose () est presque toujours inutile.
Il n'est pas nécessaire d'annuler les références d'objet créées dans une méthode. Ils seront effacés automatiquement une fois la méthode terminée. L'exception à cette règle est si vous utilisez une méthode très longue ou une boucle massive et que vous devez vous assurer que certaines références sont effacées avant la fin de la méthode. Encore une fois, ces cas sont extrêmement rares.
Je dirais que la grande majorité du temps, vous n'aurez pas besoin d'annuler les références. Essayer de déjouer le garbage collector est inutile. Vous vous retrouverez avec un code inefficace et illisible.
Au moins en Java, ce n'est pas du tout une programmation vaudou. Lorsque vous créez un objet dans Java en utilisant quelque chose comme
Foo bar = new Foo();
vous faites deux choses: premièrement, vous créez une référence à un objet, et deuxièmement, vous créez l'objet Foo lui-même. Tant que cette référence ou une autre existe, l'objet spécifique ne peut pas être gc'd. cependant, lorsque vous attribuez null à cette référence ...
bar = null ;
et en supposant que rien d'autre n'a une référence à l'objet, il est libéré et disponible pour gc la prochaine fois que le garbage collector passe.
Ça dépend.
D'une manière générale, plus vous gardez de références à vos objets, plus vite ils seront collectés.
Si l'exécution de votre méthode prend, disons, 2 secondes et que vous n'avez plus besoin d'un objet après une seconde d'exécution de la méthode, il est logique de supprimer toute référence à celui-ci. Si GC voit qu'après une seconde, votre objet est toujours référencé, la prochaine fois il pourrait le vérifier dans une minute environ.
Quoi qu'il en soit, la définition de toutes les références à null par défaut est pour moi une optimisation prématurée et personne ne devrait le faire, sauf dans de rares cas spécifiques où cela diminue de manière mesurable la consommation de mémoire.
Définir explicitement une référence à null au lieu de simplement laisser la variable sortir de la portée, n'aide pas le garbage collector, sauf si l'objet détenu est très grand, où le définir à null dès que vous avez terminé est une bonne idée.
Généralement, la définition de références à null signifie pour le LECTEUR du code que cet objet est complètement fait et ne devrait plus être concerné.
Un effet similaire peut être obtenu en introduisant une portée plus étroite en mettant un ensemble supplémentaire de croisillons
{
int l;
{ // <- here
String bigThing = ....;
l = bigThing.length();
} // <- and here
}
cela permet au bigThing d'être récupéré juste après avoir quitté les accolades imbriquées.
public class JavaMemory {
private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);
public void f() {
{
byte[] data = new byte[dataSize];
//data = null;
}
byte[] data2 = new byte[dataSize];
}
public static void main(String[] args) {
JavaMemory jmp = new JavaMemory();
jmp.f();
}
}
Le programme ci-dessus lance OutOfMemoryError
. Si vous ne commentez pas data = null;
, le OutOfMemoryError
est résolu. Il est toujours recommandé de définir la variable inutilisée sur null
Je travaillais sur une application de vidéoconférence une fois et j'ai remarqué une énorme énorme différence de performances lorsque j'ai pris le temps de supprimer les références dès que je n'avais plus besoin de l'objet. C'était en 2003-2004 et je ne peux qu'imaginer que le GC est devenu encore plus intelligent depuis. Dans mon cas, j'avais des centaines d'objets qui entraient et sortaient hors de portée chaque seconde, alors j'ai remarqué le GC quand il entrait périodiquement. Cependant, après avoir fait un point sur les objets nuls, le GC a cessé de suspendre mon application.
Cela dépend donc de ce que vous faites ...
Oui.
Extrait de "The Pragmatic Programmer" p.292:
En définissant une référence à NULL, vous réduisez le nombre de pointeurs vers l'objet d'une unité ... (ce qui permettra au garbage collector de le supprimer)
Je suppose que le PO fait référence à des choses comme ceci:
private void Blah()
{
MyObj a;
MyObj b;
try {
a = new MyObj();
b = new MyObj;
// do real work
} finally {
a = null;
b = null;
}
}
Dans ce cas, le VM ne les marquera-t-il pas pour le GC dès qu'ils quittent la portée?)
Ou, d'un autre point de vue, le fait de définir explicitement les éléments sur null leur ferait-il obtenir le GC avant qu'ils ne le fassent s'ils venaient juste d'être hors de portée? Si c'est le cas, le VM peut passer du temps à GC'ing l'objet lorsque la mémoire n'est pas de toute façon nécessaire, ce qui entraînerait en fait une mauvaise utilisation du processeur car il serait GC'ing plus tôt.
Lors de l'exécution future de votre programme, les valeurs de certains membres de données seront utilisées pour calculer une sortie visible à l'extérieur du programme. D'autres pourraient ou non être utilisés, en fonction des contributions futures (et impossibles à prévoir) au programme. Les autres données membres peuvent être garanties de ne pas être utilisées. Toutes les ressources, y compris la mémoire, allouées à ces données inutilisées sont gaspillées. Le travail du garbage collector (GC) est d'éliminer cette mémoire gaspillée. Il serait désastreux pour le GC d'éliminer quelque chose qui était nécessaire, donc l'algorithme utilisé pourrait être conservateur, en conservant plus que le strict minimum. Il peut utiliser des optimisations heuristiques pour améliorer sa vitesse, au prix de la conservation de certains éléments qui ne sont pas réellement nécessaires. Il existe de nombreux algorithmes potentiels que le GC pourrait utiliser. Par conséquent, il est possible que les modifications que vous apportez à votre programme, et qui n'affectent pas l'exactitude de votre programme, puissent néanmoins affecter le fonctionnement du GC, soit en le rendant plus rapide pour effectuer le même travail, soit pour identifier plus tôt les éléments inutilisés. Donc, ce type de changement, définir une référence d'objet inhabituel à null
, en théorie n'est pas toujours vaudou.
Est-ce du vaudou? Il y aurait des parties du code de bibliothèque Java Java qui font cela. Les auteurs de ce code sont bien meilleurs que les programmeurs moyens et connaissent ou coopèrent avec des programmeurs qui connaissent les détails des implémentations du garbage collector Donc, cela suggère qu'il y a est parfois un avantage.
Même si l'annulation de la référence était légèrement plus efficace, cela vaudrait-il la peine d'avoir à poivrer votre code avec ces horribles annulations? Ils ne feraient qu'encombrer et obscurcir le code d'intention qui les contient.
C'est une base de code rare qui n'a pas de meilleur candidat pour l'optimisation que d'essayer de déjouer le garbage collector (plus rares sont encore les développeurs qui réussissent à le déjouer). Vos efforts seront probablement mieux dépensés ailleurs, abandonnant cet analyseur Xml cruel ou trouvant une opportunité de mettre en cache le calcul. Ces optimisations seront plus faciles à quantifier et ne vous obligeront pas à salir votre base de code avec du bruit.
" Cela dépend "
Je ne connais pas Java mais en .net (C #, VB.net ...) il n'est généralement pas nécessaire d'attribuer un null lorsque vous n'avez plus besoin d'un objet.
Notez cependant qu'il n'est "généralement pas requis".
En analysant votre code, le compilateur .net fait une bonne évaluation de la durée de vie de la variable ... pour savoir avec précision quand l'objet n'est plus utilisé. Donc, si vous écrivez obj = null, il pourrait en fait sembler que l'obj est toujours utilisé ... dans ce cas, il est contre-productif d'attribuer un null.
Il y a quelques cas où cela pourrait réellement aider à affecter un null. Un exemple est que vous avez un énorme code qui s'exécute pendant longtemps ou une méthode qui s'exécute dans un thread différent, ou une boucle. Dans de tels cas, il peut être utile d'affecter null afin qu'il soit facile pour le GC de savoir qu'il n'est plus utilisé.
Il n'y a pas de règle stricte pour cela. En passant par l'endroit ci-dessus, null-assigns dans votre code et exécutez un profileur pour voir si cela aide de quelque manière que ce soit. Vous ne verrez probablement aucun avantage.
Si c'est du code .net que vous essayez d'optimiser, mon expérience a montré que prendre bien soin des méthodes Dispose et Finalize est en fait plus bénéfique que de se soucier des valeurs nulles.
Quelques références sur le sujet:
http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx
http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx