La définition d'objets Java sur null fait-elle plus rien?


112

J'étais en train de parcourir de vieux livres et j'ai trouvé une copie de "Practical Java" de Peter Hagger. Dans la section performances, il existe une recommandation pour définir des références d'objet nulllorsqu'elles ne sont plus nécessaires.

En Java, la définition de références d'objet pour nullaméliorer les performances ou l'efficacité du garbage collection? Si oui, dans quels cas est-ce un problème? Classes de conteneurs? Composition d'objets? Classes internes anonymes?

Je vois cela assez souvent dans le code. Ce conseil de programmation est-il désormais obsolète ou est-il toujours utile?


2
Profil-le. Sur les environnements d'exécution modernes, vous ne devriez voir aucune augmentation significative des performances ou de l'empreinte mémoire.
Jason Coco

12
@Jason, profiler? Cela suppose que je vais profiler un ensemble suffisamment grand de cas pour obtenir un ensemble de résultats suffisamment bon pour répondre à cela. Et que je ne choisis pas un ensemble de cas où la VM est suffisamment optimisée pour masquer les problèmes de gc et de performances. C'est pourquoi je pose cette question ici. Pour avoir une idée des cas où cela pose problème.
sal

Réponses:


73

Cela dépend un peu du moment où vous envisagez d'annuler la référence.

Si vous avez une chaîne d'objets A-> B-> C, une fois que A n'est pas joignable, A, B et C seront tous éligibles pour le garbage collection (en supposant que rien d'autre ne fait référence à B ou C). Il n'est pas nécessaire, et n'a jamais été nécessaire, de définir explicitement les références A-> B ou B-> C à null, par exemple.

En dehors de cela, la plupart du temps, le problème ne se pose pas vraiment, car en réalité, vous avez affaire à des objets dans des collections. Vous devriez généralement toujours penser à supprimer des objets des listes, des cartes, etc. en appelant la méthode remove () appropriée.

Le cas où il y avait des conseils pour définir des références à null était spécifiquement dans une longue portée où un objet gourmand en mémoire cessait d'être utilisé à mi-chemin dans la portée . Par exemple:

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

Le raisonnement ici était que parce que obj est toujours dans la portée, alors sans l'annulation explicite de la référence, il ne devient pas récupérable avant la fin de la méthode doSomethingElse () . Et c'est le conseil qui ne tient probablement plus sur les JVM modernes : il s'avère que le compilateur JIT peut déterminer à quel point une référence d'objet local donnée n'est plus utilisée.


2
Les variables locales peuvent être optimisées par la machine. Les variables de classe ne peuvent pas. J'ai vu des threads de travail (sur un pool de threads) ne pas libérer leurs objets d'état après avoir traité la demande et épingler ainsi ces objets en mémoire jusqu'à ce que le thread de travail reçoive une nouvelle tâche (qui remplace rapidement les anciens objets d'état par de nouveaux). Avec de gros objets d'état et des centaines de threads de travail (pensez: gros serveur http), cela peut représenter d'énormes quantités de mémoire détenues et copiées sans jamais être utilisées à nouveau.
Brian White

1
Je devrais probablement juste ajouter: le conseil que je donne ci-dessus est le conseil à suivre en général, en supposant des bibliothèques bien comportées, une VM bien comportée, etc. un travail est terminé, eh bien ... c'est un bogue dans ladite bibliothèque de pool de threads qui pourrait peut-être être contourné en annulant une référence d'objet, mais qui pourrait également être contourné en utilisant une bibliothèque de pool de threads moins boguée. Je ne suis pas sûr qu'un tel bug change les principes généraux de conception.
Neil Coffey

25

Non, ce n'est pas un conseil obsolète. Les références pendantes sont toujours un problème, surtout si vous implémentez, par exemple, un conteneur de tableau extensible ( ArrayListou similaire) en utilisant un tableau pré-alloué. Les éléments au-delà de la taille "logique" de la liste doivent être annulés, sinon ils ne seront pas libérés.

Voir Effective Java 2nd ed, Item 6: Eliminate Obsolete Object References.


S'agit-il d'un problème de langue ou d'un problème d'implémentation de VM?
Pablo Santa Cruz

4
C'est un problème «sémantique». Fondamentalement, parce que vous avez préalloué un tableau, la VM le voit. Il ne sait rien de la taille «logique» de votre conteneur. Supposons que vous ayez un ArrayList de taille 10, soutenu par un tableau de 16 éléments. La VM ne peut pas savoir que les éléments 10..15 ne sont pas réellement utilisés; si ces emplacements ont du contenu, ils ne seront pas libérés.
Chris Jester-Young

qu'en est-il en dehors des classes de conteneurs? Dans la composition d'objet où l'objet interne n'est pas désalloué par l'objet externe.
sal

7
@sal La règle générale est que si vous ne pouvez pas le référencer, il est récupéré. Ainsi, si l'objet externe contient une référence à un autre objet, en supposant que l'objet interne n'a pas d'autres références, la définition de la seule et unique référence de l'objet externe sur null entraînera la récupération de la mémoire de l'objet entier, y compris ses références orphelines.
ubermensch

2
Dans le même point 6 que vous avez mentionné: l' annulation des références d'objet devrait être l'exception plutôt que la norme.
ACV

10

Champs d'instance, éléments de tableau

S'il existe une référence à un objet, il ne peut pas être récupéré. Surtout si cet objet (et tout le graphique derrière) est gros, il n'y a qu'une seule référence qui arrête le ramasse-miettes, et cette référence n'est plus vraiment nécessaire, c'est une situation malheureuse.

Les cas pathologiques sont l'objet qui conserve une instance unnessary à l'ensemble de l'arborescence DOM XML qui a été utilisée pour le configurer, le MBean qui n'a pas été désenregistré, ou la référence unique à un objet d'une application Web non déployée qui empêche tout un chargeur de classe d'être déchargé .

Donc, à moins que vous ne soyez sûr que l'objet qui contient la référence lui-même sera de toute façon récupéré (ou même alors), vous devez annuler tout ce dont vous n'avez plus besoin.

Variables de portée:

Si vous envisagez de définir une variable locale sur null avant la fin de sa portée, afin qu'elle puisse être récupérée par le garbage collector et la marquer comme "inutilisable à partir de maintenant", vous devriez plutôt envisager de la placer dans une portée plus limitée. .

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

devient

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

Les portées longues et plates sont généralement mauvaises pour la lisibilité du code. Il n'est pas rare non plus d'introduire des méthodes privées pour briser les choses juste à cette fin.


Si vous faites référence à une référence forte, oui, c'est vrai, mais pas pour toutes les références. Les références faibles en java peuvent être récupérées.
James Drinkard

5

Dans les environnements à mémoire restreinte (par exemple les téléphones portables), cela peut être utile. En définissant null, l'objetc n'a pas besoin d'attendre que la variable soit hors de portée pour être gc'd.

Pour la programmation de tous les jours, cependant, cela ne devrait pas être la règle, sauf dans des cas particuliers comme celui cité par Chris Jester-Young.


1

Premièrement, cela ne signifie rien sur lequel vous définissez un objet null. Je l'explique ci-dessous:

List list1 = new ArrayList();
List list2 = list1;

Dans le segment de code ci-dessus, nous créons le nom list1de la variable de référence d' ArrayListobjet de l' objet qui est stocké dans la mémoire. Donc list1fait référence à cet objet et ce n'est rien de plus qu'une variable. Et dans la deuxième ligne de code, nous copions la référence de list1to list2. Revenons maintenant à votre question si je le fais:

list1 = null;

cela signifie qu'il list1ne fait plus référence à aucun objet qui est stocké dans la mémoire et list2n'aura donc plus rien à référencer. Donc, si vous vérifiez la taille de list2:

list2.size(); //it gives you 0

Alors voilà le concept de ramasse-miettes qui dit «vous n'avez pas à vous soucier de libérer la mémoire qui est détenue par l'objet, je le ferai quand je constaterai qu'il ne sera plus utilisé en programme et que JVM me gérera.»

J'espère que le concept est clair.


0

L'une des raisons pour cela est d'éliminer les références d'objet obsolètes. Vous pouvez lire le texte ici.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.