Работает ли сборщик мусора D? - PullRequest
4 голосов
/ 09 января 2012

Поэтому я попытался проверить, правильно ли работает сборщик мусора D, запустив эту программу в Windows.

DMD 2.057 и 2.058 бета оба дают одинаковый результат, независимо от того, указан я или нет -release, -inline, -O и т. Д.

Код:

import core.memory, std.stdio;

extern(Windows) int GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);

struct MEMORYSTATUSEX
{
    uint Length, MemoryLoad;
    ulong TotalPhys, AvailPhys, TotalPageFile, AvailPageFile;
    ulong TotalVirtual, AvailVirtual, AvailExtendedVirtual;
}

void testA(size_t count)
{
    size_t[] a;
    foreach (i; 0 .. count)
        a ~= i;
    //delete a;
}

void main()
{
    MEMORYSTATUSEX ms;
    ms.Length = ms.sizeof;

    foreach (i; 0 .. 32)
    {
        testA(16 << 20);
        GlobalMemoryStatusEx(ms);
        stderr.writefln("AvailPhys: %s MiB", ms.AvailPhys >>> 20);
    }
}

Вывод был:

AvailPhys: 3711 MiB
AvailPhys: 3365 MiB
AvailPhys: 3061 MiB
AvailPhys: 2747 MiB
AvailPhys: 2458 MiB
core.exception.OutOfMemoryError

Когда я раскомментировал оператор delete a;, выводбыло

AvailPhys: 3714 MiB
AvailPhys: 3702 MiB
AvailPhys: 3701 MiB
AvailPhys: 3702 MiB
AvailPhys: 3702 MiB
...

Так что я думаю, вопрос очевиден ... GC на самом деле работает?

Ответы [ 4 ]

8 голосов
/ 10 января 2012

Проблема здесь в ложных указателях. Сборщик мусора D консервативный, то есть он не всегда знает, что является указателем, а что нет. Иногда нужно предположить, что битовые комбинации, которые указывали бы на выделенную GC память, если интерпретировать их как указатели, являются указателями. Это в основном проблема для больших распределений, так как большие блоки являются большей целью для ложных указателей.

Вы выделяете около 48 МБ каждый раз, когда звоните testA(). По моему опыту этого достаточно, чтобы почти гарантировать, что в 32-битной системе будет ложный указатель на блок. Вероятно, вы получите лучшие результаты, если скомпилируете свой код в 64-битном режиме (поддерживается в Linux, OSX и FreeBSD, но пока не в Windows), поскольку 64-битное адресное пространство гораздо более разреженное.

Что касается моих оптимизаций GC (я - Дэвид Симча, о которых упоминает CyberShadow), то было две партии. Один в течение> 6 месяцев и не вызвал никаких проблем. Другой до сих пор рассматривается как запрос на извлечение и еще не находится в главном дереве druntime. Это, вероятно, не проблема.

В краткосрочной перспективе решение состоит в том, чтобы вручную освободить эти огромные блоки. В долгосрочной перспективе нам нужно добавить точное сканирование, хотя бы для кучи. (Точное сканирование стека является гораздо более сложной проблемой.) Я написал патч, чтобы сделать это пару лет назад, но он был отклонен, потому что он основывался на шаблонах и оценке функции времени компиляции для генерации информации о смещении указателя для каждого типа данных. Надеемся, что эта информация будет в конечном итоге сгенерирована непосредственно компилятором, и я смогу заново создать свой точный патч для сканирования кучи для сборщика мусора.

3 голосов
/ 09 января 2012

Это похоже на регрессию - это не происходит в D1 (DMD 1.069). Дэвид Симча в последнее время оптимизировал GC, так что это может быть как-то связано с этим. Пожалуйста, отправьте отчет об ошибке.

2 голосов
/ 10 января 2012

P.S. Если вы перестроите Druntime с DFLAGS, установленным в -debug=PRINTF в make-файле, вы получите информацию о том, когда GC выделяет / освобождает через консоль. :)

1 голос
/ 09 января 2012

Это работает. Текущая реализация просто никогда не освобождает память для операционной системы. Хотя GC повторно использует приобретенную память, это не является настоящей утечкой.

...