2010-04-19 2 views
6

J'ai rencontré un comportement curieux en ce qui concerne la récupération de place dans .Net.Critères de déclenchement de la récupération de place dans .Net

Le programme suivant lancera une exception OutOfMemoryException très rapidement (après moins d'une seconde sur une machine 32 bits de 2 Go). Le finaliseur Foo n'est jamais appelé.

class Foo 
{ 
    Guid guid = Guid.NewGuid(); 
    byte[] buffer = new byte[1000000]; 

    static Random rand = new Random(); 
    public Foo() 
    { 
     // Uncomment the following line and the program will run forever. 
     // rand.NextBytes(buffer); 
    } 

    ~Foo() 
    { 
     // This finalizer is never called unless the rand.NextBytes 
     // line in the constructor is uncommented. 
    } 

    static public void Main(string args[]) 
    { 
     for (; ;) 
     { 
      new Foo(); 
     } 
    } 
} 

Si la ligne rand.nextBytes est décommentée, viderait ad infinitum, et le Foo finaliseur est régulièrement invoquée. Pourquoi donc?

Ma meilleure estimation est que dans le premier cas, soit le CLR ou Windows VMM est paresseux sur l'allocation de la mémoire physique. Le tampon n'est jamais écrit, donc la mémoire physique n'est jamais utilisée. Lorsque l'espace adresse est épuisé, le système se bloque. Dans ce dernier cas, le système manque de mémoire physique avant de manquer d'espace d'adressage, le GC est déclenché et les objets sont collectés.

Cependant, voici la partie que je ne comprends pas. En supposant que ma théorie est correcte, pourquoi le GC ne se déclenche-t-il pas lorsque l'espace d'adressage est faible? Si ma théorie est incorrecte, alors quelle est la véritable explication?

Répondre

1

Le code s'exécute à 18 Mo stable sur ma machine, avec ou sans cette ligne (XP SP3 x86, .Net 3.5 SP1, dual core). Probablement ce qui se passe sur votre machine est que lorsque la ligne est commentée, le programme passe le plus clair de son temps à allouer, et parvient à allouer trop de mémoire avant que le thread garbage collector ait une chance de le libérer. Lorsque vous décommentez cette ligne, le programme consacre beaucoup moins de temps à l'allocation et ne peut donc pas allouer trop de ressources avant l'exécution du thread GC.

Essayez de remplacer la ligne commentée par Thread.Sleep(0); si ça ne plante pas, j'ai probablement raison.


Tout comme une note de côté, vous ne devriez jamais compter sur le finaliseur - il n'est pas garanti d'être appelé immédiatement lorsque l'objet est GC'ed, voire pas du tout. Au lieu de cela, dans le code réel implémenter l'interface IDisposable, et utiliser un finaliseur que si elle est extrêmement important que Dispose() être appelé, même si le programmeur a oublié (par exemple. Libérer réseau partagé/ressources de fichiers, etc.)

+0

I Je ne suis pas certain que vous ayez la bonne réponse, mais vous m'avez mis sur une piste intéressante. J'ai arrêté deux Virtual PC que je courais (tous les deux avec 512 Mo) et j'ai votre comportement. Lorsque j'ai rechargé les VPC, j'ai eu le comportement d'écrasement d'origine. –

+0

Il y a aussi des fonctions intéressantes dans la classe 'GC' à vérifier, mais encore une fois, en code réel, celles-ci ne devraient jamais être utilisées. –

+0

Avec les VPC en cours d'exécution et le Thread.Sleep (0) en place, le programme s'exécute pour toujours. Maintenant, la question se déplace ... Pourquoi le GC ne se déclenche-t-il pas automatiquement en cas d'échec de l'allocation? –

Questions connexes