J'ai un temporisateur en C # qui exécute du code à l'intérieur de sa méthode. Dans le code, j'utilise plusieurs objets temporaires.
Si j'ai quelque chose comme Foo o = new Foo();
à l'intérieur de la méthode, cela signifie-t-il que chaque fois que le chronomètre se déclenche, je crée un nouvel objet et une nouvelle référence à cet objet?
Si j'ai string foo = null
Et que je mets juste quelque chose de temporel dans foo, est-ce la même chose que ci-dessus?
Le garbage collector supprime-t-il jamais l'objet et la ou les références sont continuellement créées et restent en mémoire?
Si je déclare simplement Foo o;
Sans le pointer vers une instance, cela n'est-il pas supprimé à la fin de la méthode?
Si je veux m'assurer que tout est supprimé, quelle est la meilleure façon de le faire:
Foo o;
en dehors de la méthode du temporisateur et en faisant simplement l'affectation o = new Foo()
à l'intérieur, le pointeur vers l'objet est supprimé à la fin de la méthode, le garbage collector supprimera l'objet.1.Si j'ai quelque chose comme Foo o = new Foo (); à l'intérieur de la méthode, cela signifie-t-il que chaque fois que le chronomètre se déclenche, je crée un nouvel objet et une nouvelle référence à cet objet?
Oui.
2.Si j'ai la chaîne foo = null et que je mets juste quelque chose de temporel dans foo, est-ce la même chose que ci-dessus?
Si vous demandez si le comportement est le même, alors oui.
3. Le garbage collector supprime-t-il jamais l'objet et la ou les références sont continuellement créées et restent en mémoire?
La mémoire utilisée par ces objets est très certainement collectée une fois que les références sont considérées comme inutilisées.
4.Si je viens de déclarer Foo o; et ne pas le pointer vers une instance, n'est-ce pas disposé à la fin de la méthode?
Non, car aucun objet n'a été créé, il n'y a donc aucun objet à collecter (disposer n'est pas le bon mot).
5.Si je veux m'assurer que tout est supprimé, quelle est la meilleure façon de le faire
Si la classe de l'objet implémente IDisposable
alors vous voulez certainement appeler avec avidité Dispose
dès que possible. Le mot clé using
rend cela plus facile car il appelle automatiquement Dispose
de manière sûre.
À part cela, il n'y a vraiment rien d'autre à faire que d'arrêter d'utiliser l'objet. Si la référence est une variable locale, lorsqu'elle sortira du champ d'application, elle pourra être collectée.1 S'il s'agit d'une variable de niveau classe, vous devrez peut-être lui affecter null
pour la rendre éligible avant que la classe contenante ne soit éligible.
1C'est techniquement incorrect (ou au moins un peu trompeur). Un objet peut être éligible à la collecte bien avant qu'il ne soit hors de portée. Le CLR est optimisé pour collecter de la mémoire lorsqu'il détecte qu'une référence n'est plus utilisée. Dans les cas extrêmes, le CLR peut collecter un objet même si l'une de ses méthodes est toujours en cours d'exécution!
Mise à jour:
Voici un exemple qui démontre que le GC collectera des objets même s'ils peuvent encore être dans le champ d'application. Vous devez compiler une version Release et l'exécuter en dehors du débogueur.
static void Main(string[] args)
{
Console.WriteLine("Before allocation");
var bo = new BigObject();
Console.WriteLine("After allocation");
bo.SomeMethod();
Console.ReadLine();
// The object is technically in-scope here which means it must still be rooted.
}
private class BigObject
{
private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];
public BigObject()
{
Console.WriteLine("BigObject()");
}
~BigObject()
{
Console.WriteLine("~BigObject()");
}
public void SomeMethod()
{
Console.WriteLine("Begin SomeMethod");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End SomeMethod");
}
}
Sur ma machine, le finaliseur est exécuté pendant que SomeMethod
est toujours en cours d'exécution!
Le garbage collector .NET s'occupe de tout cela pour vous.
Il est capable de déterminer quand les objets ne sont plus référencés et libérera (éventuellement) la mémoire qui leur avait été allouée.
Les objets sont éligibles pour la collecte des ordures une fois qu'ils sortir du cadre devenir inaccessible (merci ben!). La mémoire ne sera libérée que si le garbage collector estime que vous manquez de mémoire.
Pour les ressources gérées, le garbage collector saura quand cela se produit et vous n'avez rien à faire.
Pour les ressources non gérées (telles que les connexions aux bases de données ou les fichiers ouverts), le garbage collector n'a aucun moyen de savoir combien de mémoire il consomme, et c'est pourquoi vous devez les libérer manuellement (en utilisant dispose, ou mieux encore le bloc using)
Si les objets ne sont pas libérés, soit vous avez beaucoup de mémoire et il n'y a pas besoin, soit vous conservez une référence à eux dans votre application, et par conséquent le garbage collector ne les libérera pas (au cas où vous utilisez réellement cette référence, vous entretenu)
Voici un bref aperçu:
Une dernière chose: si vous déclarez Foo foo;
sans l'attribuer, vous n'avez pas à vous inquiéter - rien ne fuit. Si Foo est un type de référence, rien n'a été créé. Si Foo est un type de valeur, il est alloué sur la pile et sera donc automatiquement nettoyé.
Foo
ne prendra jamais de mémoire.using
appelle simplement dispose sur un objet IDisposable
à sa sortie, c'est donc l'équivalent de votre deuxième puce. Les deux indiqueront que vous avez terminé avec l'objet et diront au GC que vous êtes prêt à le lâcher. Le remplacement de la seule référence à l'objet aura un effet similaire.Répondons à vos questions une par une.
Le ramasse-miettes viendra et nettoiera tout ce qui n'y fait plus référence. À moins que vous ne disposiez de ressources non gérées dans Foo
, appeler Dispose ou utiliser une instruction using dessus ne vous aidera pas vraiment.
Je suis assez sûr que cela s'applique, car il était toujours en C #. Mais, j'ai suivi un cours de conception de jeu en utilisant XNA et nous avons passé un peu de temps à parler du garbage collector pour C #. La collecte des ordures est coûteuse, car vous devez vérifier si vous avez des références à l'objet que vous souhaitez collecter. Ainsi, le GC essaie de reporter cela aussi longtemps que possible. Donc, tant que vous ne manquiez pas de mémoire physique lorsque votre programme est passé à 700 Mo, il se peut que le GC soit paresseux et ne s'en préoccupe pas encore.
Mais, si vous utilisez simplement Foo o
En dehors de la boucle et créez une o = new Foo()
à chaque fois, tout devrait fonctionner correctement.
Comme le souligne Brian, le GC peut collecter tout ce qui est inaccessible, y compris les objets qui sont toujours dans la portée et même pendant que les méthodes d'instance de ces objets sont toujours en cours d'exécution. considérez le code suivant:
class foo
{
static int liveFooInstances;
public foo()
{
Interlocked.Increment(ref foo.liveFooInstances);
}
public void TestMethod()
{
Console.WriteLine("entering method");
while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
{
Console.WriteLine("running GC.Collect");
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine("exiting method");
}
~foo()
{
Console.WriteLine("in ~foo");
Interlocked.Decrement(ref foo.liveFooInstances);
}
}
class Program
{
static void Main(string[] args)
{
foo aFoo = new foo();
aFoo.TestMethod();
//Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
}
}
s'il est exécuté avec une version de débogage, avec le débogueur attaché ou avec la ligne spécifiée, TestMethod ne sera jamais renvoyé. Mais fonctionner sans un débogueur attaché TestMethod reviendra.