Критерии запуска сборки мусора в .Net - PullRequest
6 голосов
/ 20 апреля 2010

Я столкнулся с любопытным поведением в отношении сборки мусора в .Net.

Следующая программа очень быстро сгенерирует исключение OutOfMemoryException (менее чем за секунду на 32-разрядном компьютере объемом 2 ГБ). Финализатор Foo никогда не вызывается.

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();
        }
    }
}

Если строка rand.nextBytes не закомментирована, она будет выполняться до бесконечности, и финализатор Foo будет регулярно вызываться. Почему это?

По-моему, в первом случае CLR или Windows VMM лениво выделяют физическую память. В буфер никогда не записываются, поэтому физическая память никогда не используется. Когда адресное пространство заканчивается, система падает. В последнем случае системе не хватает физической памяти, прежде чем она исчерпает адресное пространство, запускается сборщик мусора и объекты собираются.

Однако вот часть, которую я не понимаю. Если предположить, что моя теория верна, почему GC не срабатывает, когда адресное пространство заканчивается? Если моя теория неверна, то каково истинное объяснение?

1 Ответ

1 голос
/ 20 апреля 2010

Код работает со стабильными 18 МБ на моей машине, с этой строкой или без нее (XP SP3 x86, .Net 3.5 SP1, двухъядерный) .

Вероятно, что происходит наваша машина состоит в том, что, когда строка закомментирована, программа тратит большую часть своего времени на выделение, и ей удается выделить слишком много памяти, прежде чем поток сборщика мусора сможет освободить ее.Когда вы раскомментируете эту строку, программа тратит гораздо меньше времени и, следовательно, не может выделить слишком много, прежде чем запускается поток GC.

Попробуйте заменить закомментированную строку на Thread.Sleep(0);если не произойдет сбой, я, вероятно, прав.


Так же, как примечание, вы никогда не должны полагаться на финализатор - он не гарантированно вызывается сразу, когда объектИли даже вообще.Вместо этого в реальном коде реализуйте интерфейс IDisposable и используйте финализатор, только если крайне важно, чтобы вызывался Dispose(), даже если программист забыл его (например, освобождает общие ресурсы сети / файла)и т. д.)

...