Devez-vous vous débarrasser des objets et les mettre à null?


310

Avez-vous besoin de disposer des objets et de les mettre à zéro, ou le garbage collector les nettoiera-t-il lorsqu'ils sortiront du champ d'application?


4
Il semble y avoir un consensus selon lequel vous n'avez pas besoin de définir l'objet sur null, mais devez-vous faire Dispose ()?
CJ7


9
Mon conseil est toujours de disposer si un objet implémente IDisposable. Utilisez un bloc à chaque fois. Ne faites pas d'hypothèses, ne laissez pas au hasard. Cependant, vous n'avez pas besoin de mettre les choses à zéro. Un objet vient de sortir de sa portée.
peter

11
@peter: N'utilisez pas les blocs "using" avec les proxys client WCF: msdn.microsoft.com/en-us/library/aa355056.aspx
nlawalker

9
CEPENDANT, VOUS POUVEZ vouloir définir certaines références à null dans votre Dispose()méthode! Il s'agit d'une variation subtile sur cette question, mais importante parce que l'objet étant disposé ne peut pas savoir s'il "sort de la portée" (l'appel Dispose()n'est pas une garantie). Plus ici: stackoverflow.com/questions/6757048/…
Kevin P. Rice

Réponses:


239

Les objets seront nettoyés lorsqu'ils ne seront plus utilisés et lorsque le ramasse-miettes le jugera bon. Parfois, vous devrez peut-être définir un objet pour le nullrendre hors de portée (comme un champ statique dont vous n'avez plus besoin de la valeur), mais dans l'ensemble, il n'est généralement pas nécessaire de le définir surnull .

Concernant l'élimination des objets, je suis d'accord avec @Andre. Si l'objet est, IDisposableil est judicieux de le supprimer lorsque vous n'en avez plus besoin, surtout si l'objet utilise des ressources non gérées. Ne pas disposer de ressources non gérées entraînera des fuites de mémoire .

Vous pouvez utiliser l' usinginstruction pour supprimer automatiquement un objet une fois que votre programme quitte la portée de l' usinginstruction.

using (MyIDisposableObject obj = new MyIDisposableObject())
{
    // use the object here
} // the object is disposed here

Qui est fonctionnellement équivalent à:

MyIDisposableObject obj;
try
{
    obj = new MyIDisposableObject();
}
finally
{
    if (obj != null)
    {
        ((IDisposable)obj).Dispose();
    }
}

4
Si obj est un type de référence, alors le bloc finally est équivalent à:if (obj != null) ((IDisposable)obj).Dispose();
Randy prend en charge Monica

1
@Tuzo: Merci! Modifié pour refléter cela.
Zach Johnson

2
Une remarque concernant IDisposable. Ne pas disposer d'un objet ne provoquera généralement pas de fuite de mémoire sur une classe bien conçue. Lorsque vous travaillez avec des ressources non managées en C #, vous devez disposer d'un finaliseur qui libérera toujours les ressources non managées. Cela signifie qu'au lieu de désallouer les ressources lorsque cela doit être fait, il sera reporté au moment où le garbage collector finalise l'objet géré. Cela peut néanmoins causer de nombreux autres problèmes (tels que des verrous non libérés). Vous devriez jeter un IDisposablebien!
Aidiakapi

@RandyLevy Avez-vous une référence pour cela? Merci
Basic

Mais ma question est Dispose () doit implémenter une logique? Doit-il faire quelque chose? Ou en interne lorsque Dispose () est appelé signaux GC qui est bon pour aller? J'ai vérifié le code source du TextWriter par exemple et Dispose n'a aucune implémentation.
Mihail Georgescu

137

Les objets ne sortent jamais de la portée en C # comme ils le font en C ++. Ils sont traités automatiquement par le garbage collector lorsqu'ils ne sont plus utilisés. Il s'agit d'une approche plus compliquée que C ++ où la portée d'une variable est entièrement déterministe. Le ramasse-miettes CLR passe activement en revue tous les objets qui ont été créés et détermine s'ils sont utilisés.

Un objet peut sortir "hors de portée" dans une fonction mais si sa valeur est renvoyée, GC vérifiera si la fonction appelante conserve ou non la valeur de retour.

Il nulln'est pas nécessaire de définir des références d'objet car le garbage collection fonctionne en déterminant quels objets sont référencés par d'autres objets.

En pratique, vous n'avez pas à vous soucier de la destruction, cela fonctionne et c'est génial :)

Disposedoit être appelé sur tous les objets qui implémentent IDisposablelorsque vous avez fini de travailler avec eux. Normalement, vous utiliseriez un usingbloc avec ces objets comme ceci:

using (var ms = new MemoryStream()) {
  //...
}

EDIT Sur portée variable. Craig a demandé si la portée variable a un effet sur la durée de vie de l'objet. Pour expliquer correctement cet aspect de CLR, je vais devoir expliquer quelques concepts de C ++ et C #.

Portée variable réelle

Dans les deux langues, la variable ne peut être utilisée que dans la même portée que celle définie - classe, fonction ou bloc d'instructions entouré d'accolades. La différence subtile, cependant, est qu'en C #, les variables ne peuvent pas être redéfinies dans un bloc imbriqué.

En C ++, c'est parfaitement légal:

int iVal = 8;
//iVal == 8
if (iVal == 8){
    int iVal = 5;
    //iVal == 5
}
//iVal == 8

En C #, cependant, vous obtenez une erreur de compilation:

int iVal = 8;
if(iVal == 8) {
    int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}

Cela a du sens si vous regardez le MSIL généré - toutes les variables utilisées par la fonction sont définies au début de la fonction. Jetez un œil à cette fonction:

public static void Scope() {
    int iVal = 8;
    if(iVal == 8) {
        int iVal2 = 5;
    }
}

Ci-dessous se trouve l'IL généré. Notez que iVal2, qui est défini à l'intérieur du bloc if est réellement défini au niveau de la fonction. En fait, cela signifie que C # n'a qu'une portée de niveau classe et fonction en ce qui concerne la durée de vie variable.

.method public hidebysig static void  Scope() cil managed
{
  // Code size       19 (0x13)
  .maxstack  2
  .locals init ([0] int32 iVal,
           [1] int32 iVal2,
           [2] bool CS$4$0000)

//Function IL - omitted
} // end of method Test2::Scope

Portée C ++ et durée de vie de l'objet

Chaque fois qu'une variable C ++, allouée sur la pile, sort du cadre, elle est détruite. N'oubliez pas qu'en C ++, vous pouvez créer des objets sur la pile ou sur le tas. Lorsque vous les créez sur la pile, une fois que l'exécution quitte la portée, ils sont extraits de la pile et détruits.

if (true) {
  MyClass stackObj; //created on the stack
  MyClass heapObj = new MyClass(); //created on the heap
  obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives

Lorsque des objets C ++ sont créés sur le tas, ils doivent être explicitement détruits, sinon c'est une fuite de mémoire. Pas un tel problème avec les variables de pile cependant.

Durée de vie de l'objet C #

Dans CLR, les objets (c'est-à-dire les types de référence) sont toujours créés sur le tas géré. Ceci est encore renforcé par la syntaxe de création d'objet. Considérez cet extrait de code.

MyClass stackObj;

En C ++, cela créerait une instance MyClasssur la pile et appellerait son constructeur par défaut. En C #, cela créerait une référence à la classe MyClassqui ne pointe vers rien. La seule façon de créer une instance d'une classe est d'utiliser l' newopérateur:

MyClass stackObj = new MyClass();

D'une certaine manière, les objets C # ressemblent beaucoup à des objets créés en utilisant la newsyntaxe en C ++ - ils sont créés sur le tas mais contrairement aux objets C ++, ils sont gérés par le runtime, vous n'avez donc pas à vous soucier de les détruire.

Étant donné que les objets sont toujours sur le tas, le fait que les références d'objet (c'est-à-dire les pointeurs) sortent de la portée devient inutile. Il y a plus de facteurs impliqués pour déterminer si un objet doit être collecté que la simple présence de références à l'objet.

Références d'objets C #

Jon Skeet a comparé les références d'objets en Java aux morceaux de chaîne attachés au ballon, qui est l'objet. La même analogie s'applique aux références d'objets C #. Ils pointent simplement vers un emplacement du tas qui contient l'objet. Ainsi, le définir sur null n'a pas d'effet immédiat sur la durée de vie de l'objet, le ballon continue d'exister, jusqu'à ce que le GC le "fasse éclater".

En poursuivant l'analogie avec le ballon, il semblerait logique qu'une fois que le ballon n'a pas de cordes, il peut être détruit. En fait, c'est exactement ainsi que fonctionnent les objets comptés par référence dans les langages non gérés. Sauf que cette approche ne fonctionne pas très bien pour les références circulaires. Imaginez deux ballons qui sont attachés ensemble par une chaîne, mais aucun ballon n'a de chaîne pour autre chose. Selon de simples règles de comptage des références, ils continuent d'exister tous les deux, même si l'ensemble du groupe de ballons est "orphelin".

Les objets .NET ressemblent beaucoup à des ballons à l'hélium sous un toit. Lorsque le toit s'ouvre (GC fonctionne) - les ballons inutilisés flottent, même s'il peut y avoir des groupes de ballons attachés ensemble.

Le GC .NET utilise une combinaison de GC générationnel et de marquage et balayage. L'approche générationnelle implique que le runtime favorise l'inspection des objets qui ont été alloués le plus récemment, car ils sont plus susceptibles d'être inutilisés et le marquage et le balayage impliquent que le runtime parcourt le graphique d'objet entier et détermine s'il existe des groupes d'objets inutilisés. Cela traite adéquatement le problème de dépendance circulaire.

En outre, .NET GC s'exécute sur un autre thread (ce que l'on appelle le thread de finalisation) car il a beaucoup à faire et le faire sur le thread principal interromprait votre programme.


1
@Igor: En allant «hors de portée», je veux dire que la référence d'objet est hors contexte et ne peut pas être référencée dans la portée actuelle. Cela se produit sûrement toujours en C #.
CJ7

@Craig Johnston, ne confondez pas la portée des variables utilisée par le compilateur avec la durée de vie variable qui est déterminée par le runtime - elles sont différentes. Une variable locale peut ne pas être "active" même si elle est toujours dans la portée.
Randy prend en charge Monica

1
@Craig Johnston: Voir blogs.msdn.com/b/ericgu/archive/2004/07/23/192842.aspx : "il n'y a aucune garantie qu'une variable locale restera active jusqu'à la fin d'une étendue si ce n'est pas le cas Le runtime est libre d'analyser le code dont il dispose et de déterminer ce qu'il n'y a plus d'autres utilisations d'une variable au-delà d'un certain point, et donc de ne pas conserver cette variable en direct au-delà de ce point (c'est-à-dire de ne pas la traiter comme une racine aux fins de GC). "
Randy prend en charge Monica le

1
@Tuzo: Vrai. C'est à cela que sert GC.KeepAlive.
Steven Sudit

1
@Craig Johnston: Non et oui. Non, car le runtime .NET gère cela pour vous et fait du bon travail. Oui parce que le travail du programmeur n'est pas d'écrire du code qui se compile (juste) mais d'écrire du code qui s'exécute . Parfois, il est utile de savoir ce que le runtime fait sous les couvertures (par exemple, dépannage). On pourrait dire que c'est le type de connaissances qui aide à séparer les bons programmeurs des grands programmeurs.
Randy prend en charge Monica le

18

Comme d'autres l'ont dit, vous voulez certainement appeler Disposesi la classe implémente IDisposable. Je prends une position assez rigide à ce sujet. Certains prétendent que l' appel de puissance Disposesur DataSet, par exemple, est inutile parce qu'ils démontées et vu qu'il n'a pas fait quelque chose de significatif. Mais, je pense qu'il y a des erreurs dans cet argument.

Lisez ceci pour un débat intéressant par des personnes respectées sur le sujet. Lisez ensuite mon raisonnement ici pourquoi je pense que Jeffery Richter est dans le mauvais camp.

Maintenant, pour savoir si vous devez ou non définir une référence null. La réponse est non. Permettez-moi d'illustrer mon propos avec le code suivant.

public static void Main()
{
  Object a = new Object();
  Console.WriteLine("object created");
  DoSomething(a);
  Console.WriteLine("object used");
  a = null;
  Console.WriteLine("reference set to null");
}

Alors, quand pensez-vous que l'objet référencé par aest éligible pour la collecte? Si vous avez dit après l'appel, a = nullvous vous trompez. Si vous avez dit une fois la Mainméthode terminée, vous vous trompez également. La bonne réponse est qu'il est éligible à la collecte au cours de l'appel à DoSomething. C'est vrai. Il est éligible avant la définition de la référence nullet peut-être même avant la fin de l'appel à DoSomething. En effet, le compilateur JIT peut reconnaître quand les références d'objet ne sont plus déréférencées même si elles sont toujours enracinées.


3
Que faire si aun champ membre privé dans une classe? Si an'est pas défini sur null, le GC n'a aucun moyen de savoir s'il asera à nouveau utilisé dans une méthode, non? Par conséquent a, ne sera pas collecté tant que la classe entière ne sera pas collectée. Non?
Kevin P. Rice

4
@Kevin: Correct. Si vous aétiez un membre de la classe et que la classe contenant aétait toujours enracinée et en cours d'utilisation, elle resterait également. C'est un scénario où le définir nullpourrait être bénéfique.
Brian Gideon

1
Votre point est lié à une raison pour laquelle il Disposeest important - il n'est pas possible d'invoquer Dispose(ou toute autre méthode non inlineable) sur un objet sans référence enracinée à celui-ci; appeler Disposeune fois que vous avez terminé à l'aide d'un objet garantit qu'une référence enracinée continuera d'exister pendant toute la durée de la dernière action effectuée sur celui-ci. L'abandon de toutes les références à un objet sans appel Disposepeut ironiquement avoir pour effet de libérer occasionnellement les ressources de l'objet trop tôt .
supercat

Cet exemple et cette explication ne semblent pas définitifs sur la suggestion difficile de ne jamais définir de références à null. Je veux dire, à l'exception du commentaire de Kevin, une référence définie sur null après avoir été éliminée semble assez bénigne , alors quel est le mal? Suis-je en train de manquer quelque chose?
Dathompson

13

Vous n'avez jamais besoin de définir des objets sur null en C #. Le compilateur et le runtime se chargeront de déterminer quand ils ne sont plus dans la portée.

Oui, vous devez supprimer les objets qui implémentent IDisposable.


2
Si vous avez une référence longue durée (ou même statique) à un grand objet, vous devez l' wantannuler dès que vous en avez terminé afin qu'il soit libre d'être récupéré.
Steven Sudit

12
Si vous en avez «fini», cela ne devrait pas être statique. Si ce n'est pas statique, mais "à longue durée de vie", alors il devrait toujours sortir du champ d'application peu de temps après que vous en ayez fini. La nécessité de définir des références à null indique un problème avec la structure du code.
EMP

Vous pouvez avoir un élément statique avec lequel vous avez terminé. Considérez: Une ressource statique qui est lue à partir du disque dans un format convivial, puis analysée dans un format adapté à l'utilisation du programme. Vous pouvez vous retrouver avec une copie privée des données brutes qui ne sert plus à rien. (Exemple réel: l'analyse est une routine en deux passes et ne peut donc pas simplement traiter les données au fur et à mesure qu'elles sont lues.)
Loren Pechtel

1
Ensuite, il ne devrait pas stocker de données brutes dans un champ statique s'il n'est utilisé que temporairement. Bien sûr, vous pouvez le faire, ce n'est tout simplement pas une bonne pratique pour exactement cette raison: vous devez ensuite gérer sa durée de vie manuellement.
EMP

2
Vous l'évitez en stockant les données brutes dans une variable locale dans la méthode qui les traite. La méthode renvoie les données traitées, que vous conservez, mais le local pour les données brutes sort de la portée lorsque la méthode se termine et est automatiquement GCed.
EMP

11

Je suis d'accord avec la réponse courante ici: oui, vous devez disposer et non, vous ne devez généralement pas définir la variable sur null ... mais je voulais souligner que disposer ne concerne PAS principalement la gestion de la mémoire. Oui, cela peut aider (et le fait parfois) avec la gestion de la mémoire, mais son objectif principal est de vous fournir une libération déterministe de ressources rares.

Par exemple, si vous ouvrez un port matériel (série par exemple), un socket TCP / IP, un fichier (en mode d'accès exclusif) ou même une connexion à une base de données, vous avez maintenant empêché tout autre code d'utiliser ces éléments jusqu'à ce qu'ils soient libérés. Dispose libère généralement ces articles (ainsi que GDI et autres poignées "os", etc., dont il existe des milliers, mais qui sont globalement limités). Si vous n'appelez pas dipose sur l'objet propriétaire et libérez explicitement ces ressources, essayez à nouveau d'ouvrir la même ressource à l'avenir (ou un autre programme le fera), cette tentative d'ouverture échouera car votre objet non disposé et non collecté a toujours l'élément ouvert . Bien sûr, lorsque le GC récupère l'élément (si le modèle Dispose a été correctement mis en œuvre), la ressource sera libérée ... mais vous ne savez pas quand cela le sera, alors vous ne le faites pas '' Je ne sais pas quand il est sûr de rouvrir cette ressource. Il s'agit du principal problème que Dispose contourne. Bien sûr, la libération de ces poignées libère souvent de la mémoire aussi, et ne jamais les libérer peut ne jamais libérer cette mémoire ... d'où toutes les discussions sur les fuites de mémoire ou les retards de nettoyage de la mémoire.

J'ai vu des exemples concrets de ce problème. Par exemple, j'ai vu des applications Web ASP.Net qui ne finissent pas par se connecter à la base de données (quoique pour de courtes périodes de temps ou jusqu'à ce que le processus du serveur Web soit redémarré) parce que le «pool de connexions du serveur SQL est plein» ... c.-à-d. , de nombreuses connexions ont été créées et non explicitement publiées dans un laps de temps si court qu'aucune nouvelle connexion ne peut être créée et de nombreuses connexions du pool, bien qu'elles ne soient pas actives, sont toujours référencées par des objets non déposés et non collectés et ainsi peuvent ' t être réutilisé. L'élimination correcte des connexions à la base de données, si nécessaire, garantit que ce problème ne se produit pas (du moins pas sauf si vous disposez d' un accès simultané très élevé).


11

Si l'objet est implémenté IDisposable, alors oui, vous devez le supprimer. L'objet pourrait être accroché à des ressources natives (descripteurs de fichiers, objets OS) qui pourraient ne pas être libérées immédiatement sinon. Cela peut entraîner une famine des ressources, des problèmes de verrouillage de fichiers et d'autres bogues subtils qui pourraient autrement être évités.

Voir également Implémentation d'une méthode de suppression sur MSDN.


Mais le collectionneur de garabage n'appellera-t-il pas Dispose ()? Si oui, pourquoi auriez-vous besoin de l'appeler?
CJ7

Sauf si vous l'appelez explicitement, il n'y a aucune garantie qui Disposesera appelée. De plus, si votre objet s'accroche à une ressource rare ou verrouille une ressource (par exemple un fichier), alors vous voudrez le libérer dès que possible. Attendre que le GC fasse cela n'est pas optimal.
Chris Schmich

12
GC n'appellera jamais Dispose (). Le GC pourrait appeler un finaliseur qui, par convention, devrait nettoyer les ressources.
adrianm

@adrianm: Pas mightappeler, mais willappeler.
leppie

2
@leppie: les finaliseurs ne sont pas déterministes et peuvent ne pas être appelés (par exemple lorsque le domaine d'application est déchargé). Si vous avez besoin d'une finalisation déterministe, vous devez implémenter ce que je pense être appelé un gestionnaire critique. Le CLR a un traitement spécial de ces objets pour garantir qu'ils sont finalisés (par exemple, il pré-jits le code de finalisation pour gérer la mémoire faible)
adrianm

9

S'ils implémentent l'interface IDisposable, vous devez les supprimer. Le ramasse-miettes s'occupe du reste.

EDIT: le mieux est d'utiliser la usingcommande lorsque vous travaillez avec des articles jetables:

using(var con = new SqlConnection("..")){ ...

5

Lorsqu'un objet implémente, IDisposablevous devez appeler Dispose(ou Close, dans certains cas, cela appellera Dispose pour vous).

Normalement, vous n'avez pas besoin de définir des objets null, car le GC sait qu'un objet ne sera plus utilisé.

Il y a une exception lorsque je mets des objets à null. Lorsque je récupère un grand nombre d'objets (de la base de données) sur lesquels je dois travailler et les stocke dans une collection (ou un tableau). Lorsque le "travail" est terminé, je mets l'objet à null, car le GC ne sait pas que j'ai fini de travailler avec.

Exemple:

using (var db = GetDatabase()) {
    // Retrieves array of keys
    var keys = db.GetRecords(mySelection); 

    for(int i = 0; i < keys.Length; i++) {
       var record = db.GetRecord(keys[i]);
       record.DoWork();
       keys[i] = null; // GC can dispose of key now
       // The record had gone out of scope automatically, 
       // and does not need any special treatment
    }
} // end using => db.Dispose is called

4

Normalement, il n'est pas nécessaire de définir les champs sur null. Cependant, je recommanderais toujours de supprimer les ressources non gérées.

Par expérience, je vous conseille également de faire ce qui suit:

  • Désabonnez-vous des événements si vous n'en avez plus besoin.
  • Définissez n'importe quel champ contenant un délégué ou une expression sur null s'il n'est plus nécessaire.

J'ai rencontré des problèmes très difficiles à trouver qui étaient le résultat direct du non-respect des conseils ci-dessus.

Un bon endroit pour le faire est dans Dispose (), mais le plus tôt est généralement mieux.

En général, s'il existe une référence à un objet, le garbage collector (GC) peut prendre quelques générations de plus pour comprendre qu'un objet n'est plus utilisé. Pendant tout ce temps, l'objet reste en mémoire.

Cela peut ne pas être un problème tant que vous n'avez pas constaté que votre application utilise beaucoup plus de mémoire que vous ne le pensez. Lorsque cela se produit, connectez un profileur de mémoire pour voir quels objets ne sont pas nettoyés. La définition de champs référençant d'autres objets à null et la suppression des collections lors de leur élimination peuvent vraiment aider le GC à déterminer quels objets il peut supprimer de la mémoire. Le GC récupérera la mémoire utilisée plus rapidement, rendant votre application beaucoup moins gourmande en mémoire et plus rapide.


1
Que voulez-vous dire par «événements et délégués» - que faut-il «nettoyer» avec ces derniers?
CJ7

@Craig - J'ai modifié ma réponse. Espérons que cela clarifie un peu les choses.
Marnix van Valen

3

Appelez toujours disposer. Cela ne vaut pas le risque. Les grandes applications d'entreprise gérées doivent être traitées avec respect. Aucune hypothèse ne peut être faite sinon elle reviendra vous mordre.

N'écoutez pas leppie.

De nombreux objets n'implémentent pas réellement IDisposable, vous n'avez donc pas à vous en préoccuper. S'ils sortent vraiment du cadre, ils seront automatiquement libérés. De plus, je n'ai jamais rencontré de situation où j'ai dû mettre quelque chose à zéro.

Une chose qui peut arriver, c'est que beaucoup d'objets peuvent être maintenus ouverts. Cela peut augmenter considérablement l'utilisation de la mémoire de votre application. Parfois, il est difficile de déterminer s'il s'agit réellement d'une fuite de mémoire ou si votre application fait simplement beaucoup de choses.

Les outils de profil de mémoire peuvent aider avec des choses comme ça, mais cela peut être délicat.

De plus, désabonnez-vous toujours des événements qui ne sont pas nécessaires. Soyez également prudent avec la liaison et les contrôles WPF. Pas une situation habituelle, mais je suis tombé sur une situation où j'avais un contrôle WPF qui était lié à un objet sous-jacent. L'objet sous-jacent était volumineux et occupait une grande quantité de mémoire. Le contrôle WPF était remplacé par une nouvelle instance, et l'ancien était toujours en attente pour une raison quelconque. Cela a provoqué une fuite de mémoire importante.

Dans Hindsite, le code a été mal écrit, mais le fait est que vous voulez vous assurer que les choses qui ne sont pas utilisées sortent du cadre. Cela a pris beaucoup de temps à trouver avec un profileur de mémoire car il est difficile de savoir ce qui est valide en mémoire et ce qui ne devrait pas y être.


2

Je dois aussi répondre. Le JIT génère des tableaux avec le code à partir de son analyse statique de l'utilisation des variables. Ces entrées de table sont les "GC-Roots" dans le cadre de pile actuel. Au fur et à mesure que le pointeur d'instruction avance, ces entrées de table deviennent invalides et donc prêtes pour la récupération de place. Par conséquent: s'il s'agit d'une variable de portée, vous n'avez pas besoin de la définir sur null - le GC collectera l'objet. S'il s'agit d'un membre ou d'une variable statique, vous devez le définir sur null

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.