Je cherchais récemment à libérer de la mémoire occupée par des objets Java. Ce faisant, je ne comprenais pas comment les objets étaient copiés (peu profonds/profonds) en Java et comment éviter de supprimer/annuler accidentellement des objets pendant qu’ils étaient encore utilisés.
Envisagez les scénarios suivants:
ArrayList<Object>
comme argument à une méthode.ArrayList<Object>
à une classe exécutable pour être traité par un thread.ArrayList<Object>
dans une HashMap
.Maintenant, dans ce cas, si j'appelle list = null;
ou list.clear();
, qu'advient-il des objets? Dans quel cas les objets sont perdus et dans quels cas seule la référence est définie sur null?
Je suppose que cela a à voir avec la copie superficielle et profonde d'objets, mais dans quels cas une copie superficielle se produit-elle et dans quel cas une copie profonde se produit-elle en Java?
Tout d'abord, vous ne définissez jamais un objet sur null. Ce concept n'a pas de sens. Vous pouvez attribuer une valeur null
à une variable, mais vous devez distinguer très soigneusement les concepts de "variable" et "objet". Une fois que vous avez terminé, votre question répondra en soi :)
Maintenant, en termes de "copie superficielle" par rapport à "copie en profondeur" - il est probablement préférable d'éviter le terme "copie superficielle" ici, car une copie superficielle implique généralement la création d'un nouvel objet, mais simplement la copie directe des champs d'un objet existant. Une copie complète prendrait également une copie des objets référencés par ces champs (pour les champs de type référence). Une mission simple comme celle-ci:
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<String> list2 = list1;
... ne fait pas soit une copie superficielle ou profonde en ce sens. Il ne fait que copier la référence. Après le code ci-dessus, list1
et list2
sont des variables indépendantes - elles ont juste les mêmes valeurs (références) pour le moment. Nous pourrions changer la valeur de l'un d'eux, et cela n'affecterait pas l'autre:
list1 = null;
System.out.println(list2.size()); // Just prints 0
Si maintenant, au lieu de changer les variables, nous modifions l'objet auquel les valeurs des variables font référence, cette modification sera également visible via l'autre variable:
list2.add("Foo");
System.out.println(list1.get(0)); // Prints Foo
Revenons à votre question initiale: vous ne stockez jamais objets réels dans une carte, une liste, un tableau, etc. Vous ne stockez jamais que références. Un objet ne peut être récupéré que lorsqu'il n'y a plus aucun moyen de "vivre" du code atteignant cet objet. Donc dans ce cas:
List<String> list = new ArrayList<String>();
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("Foo", list);
list = null;
... l'objet ArrayList
ne peut toujours pas être récupéré, car Map
a une entrée qui s'y réfère.
Pour effacer la variable
À ma connaissance,
Si vous souhaitez réutiliser la variable, utilisez
Object.clear();
Si vous n'allez pas réutiliser, alors définissez
Object=null;
Remarque: Comparez avec removeAll (), clear () est plus rapide.
S'il vous plait corrigez moi si je me trompe....
Java GC réclame automatiquement les objets quand ils ne sont référencés nulle part. Donc, dans la plupart des cas, vous devrez définir explicitement la référence comme suit: null
Dès que la portée de la variable se termine, l'objet devient éligible pour GC et est libéré si aucune autre référence ne pointe vers l'objet.
Java est passe par valeur donc si vous définissez la liste sur null
dans la méthode, cela n'affectera pas la référence d'origine qui vous a été transmise dans la méthode.
public class A{
private List<Integer> list = new ArrayList<Integer>();
public static void main(String[] args) {
A a = new A();
B b = new B();
b.method(a.list);
System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException
}
}
class B{
public void method(List<Integer> list){
list = null;
//just this reference is set to null and not the original one
//so list of A will not be GCed
}
}
Si vous avez passé un ArrayList à une méthode, list = null n'aura aucun effet s'il existe une référence active à la liste quelque part, par exemple dans le code appelant. Si vous appelez list.clear () n’importe où dans le code, les références aux objets de cette liste seront annulées. Passer une référence à une méthode n'est pas superficiel; la copier passe à une valeur de référence
Cela dépend du nombre de variables qui font référence à chacun de vos objets. Pour l'expliquer, il vaudrait mieux utiliser du code:
Object myAwesomeObject = new Object();
List<Object> myList = new ArrayList<Object>();
myList.add(myAwesomeObject);
myList = null; // Your object hasn't been claimed by the GC just yet, your variable "myAwesomeObject" is still refering to it
myAwesomeObject = null; // done, now your object is eligible for garbage collection.
Cela ne dépend donc pas du fait que vous transmettiez votre ArrayList en tant qu’argument à une méthode ou similaire, cela dépend du nombre de variables qui font encore référence à vos objets.
Si vous placez la liste dans une carte de hachage, celle-ci contient désormais une référence à la liste.
Si vous transmettez la liste en tant qu'argument à une méthode, celle-ci sera référencée pour la durée de la méthode.
Si vous le transmettez à un thread à manipuler, le thread aura une référence à l'objet jusqu'à ce qu'il se termine.
Dans tous ces cas, si vous définissez list = null
, les références seront toujours conservées, mais elles disparaîtront après la disparition de ces références.
Si vous effacez simplement la liste, les références seront toujours valables, mais indiqueront maintenant une liste qui a été vidée subitement, par des moyens inconnus du programmeur et considérés comme un bogue, en particulier si vous utilisez le fil.
Je cherchais récemment à libérer de la mémoire occupée par des objets Java.
Un conseil.
C'est généralement une {mauvaise idée} _ penser à cela. Et c'est généralement une pire idée d'essayer d '"aider". Dans 99,8% des cas, le ramasse-miettes Java est capable de faire un meilleur travail de ramassage des ordures si vous le laissez juste aller de l'avant ... et ne perdez pas votre effort en assignant null
à des choses. En effet, il est probable que les champs que vous annulez se trouvent dans des objets sur le point de devenir inaccessibles de toute façon. Et dans ce cas, le GC ne va même pas regarder les champs que vous avez annulés.
Si vous prenez ce point de vue (pragmatique), toutes vos réflexions sur les copies superficielles par rapport aux copies profondes et lorsqu'il est prudent d'annuler des choses sont discutables.
Il existe un faible pourcentage de cas dans lesquels il est conseillé d'attribuer null
... pour éviter les fuites de stockage à moyen ou long terme. Et si vous êtes dans l'une de ces rares situations où il est "recyclé" d'objets est une bonne idée, l'annulation est également recommandée.