Запуск сборки мусора в Mono - PullRequest
12 голосов
/ 07 января 2012

Как заставить сборщика мусора в Mono сделать что-нибудь полезное?Внизу этого поста находится простая тестовая программа на C #, которая генерирует две большие строки.После генерации первой строки переменная разыменовывается, и область действия закрывается, а сборщик мусора запускается вручную.Несмотря на это, используемая память не уменьшается, и программа взрывается с исключением из-за нехватки памяти во время создания второй строки.

Необработанное исключение: OutOfMemoryException [ОШИБКА] FATAL UNHANDLED EXCEPTION: System.OutOfMemoryException:Недостаточно памяти в строке (управляемая оболочкой): InternalAllocateStr (int) в System.String.Concat (System.String str0, System.String str1) [0x00000] в: 0 в GCtest.Main (System.String [] args) [0x00000] in: 0

После проведения исследования я обнаружил переключатель --gc=sgen для Mono, который использует другой алгоритм сборки мусора.Это еще хуже, генерируя следующую трассировку стека:

Stacktrace:

в (управляемая оболочкой) строка. InternalAllocateStr (int) <0xffffffff> в строке .Concat(строка, строка) <0x0005b> в GCtest.Main (строка []) <0x00243> в (вызов-время выполнения оболочки) .runtime_invoke_void_object (object, intptr, intptr, intptr) <0xffffffff>

Собственная трассировка стека:

0 mono-sgen 0x000bc086 mono_handle_native_sigsegv + 422 1 mono-sgen
0x0000466e mono_sigsegv_signal_handler + 334 2 libsystem_c.dylib
0x913c659b 0ffs 0x64x068x068x064x070sgen
0x0020306d mono_gc_alloc_obj_nolock + 363 5 моно-sgen
0x0020394a mono_gc_alloc_string + 153 6 моно-sgen * +1022 * 0x001c9a10 mono_string_new_size + 147 7 моно-sgen * * 0x0022a6d1 тысячу двадцать-три ves_icall_System_String_InternalAllocateStr + 28 8 ???
0x004c450c 0x0+ 4998412 9 ???
0x004ceec4 0x0 + 5041860 10 ???
0x004c0f74 0x0 + 4984692 11 ???
0x004c1163 0x0+ 4985187 12 mono-sgen
0x00010164 mono_jit_runtime_invoke + 164 13 mono-sgen
0x001c5791 mono_runtime_invoke + 137 14 моно-sgen
0x001c7f92 mono_runtime-mon__run_t_t_t_t_t_0_sun_t_t_t_t_t_t_t_t_
0x0008c617 mono_main + 8551 17 mono-sgen
0x00002606 start + 54 18 ???
0x00000003 0x0 + 3

Отладочная информация из gdb:

/ tmp / mono-gdb-commands.2aCwlD: 1: Ошибка в исходном командном файле: невозможно отладить себя

Получил SIGSEGV при выполнении собственного кода.Обычно это указывает на фатальную ошибку во время выполнения mono или в одной из собственных библиотек, используемых вашим приложением.

/ Users / fraser / Documents / diff-match-patch / csharp / GCtest.command: строка 12: 41011Прерывание прерывания: 6 моно --gc = sgen GCtest.exe

Вот код:

using System;
public class GCtest {
  public static void Main(string[] args) {
    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");

    {
      // Generate the first string.
      string text1 = "hello old world.";
      for (int i = 0; i < 25; i++) {
        text1 = text1 + text1;
      }
      // Dereference variable.
      text1 = null;
      // Drop out of scope.
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");

    // Generate the second string.
    string text2 = "HELLO NEW WORLD!";
    for (int i = 0; i < 25; i++) {
      text2 = text2 + text2;
    }

    Console.WriteLine("Memory: " + (GC.GetTotalMemory(true) / 1024) + " KB");
  }
}

1 Ответ

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

Это 32-битный моно?Я полагаю, что описанное вами поведение вызвано тем, что с Boehm GC, по крайней мере, стек сканируется консервативно.Это означает, что значения на нем обрабатываются как если бы они были указателями.Если какое-то такое значение указывает на объект (или его подчиненный), то этот объект не будет собран.Теперь понятно, почему большие объекты здесь проблематичны - они могут легко заполнить виртуальное адресное пространство 32-битного процесса, у нас есть большой шанс, что какое-то значение в стеке указывает где-то в нем, и весь объект не собирается.Каковы источники таких противных фальшивых указателей?Наиболее известны мне хэши, случайные значения или дата / время (обычно int с низким).

Как заставить сборщик мусора в Mono делать что-нибудь полезное?

Вы использовали правильный метод, но я думаю, что вы столкнулись с проблемой, описанной выше.Обычные программы не так сильно страдают, потому что (так) огромные объекты довольно редки.Завтра я также протестирую его на 64-битном Mono.

Тем не менее, возникает вопрос, который возникает автоматически: почему проект Mono не создает еще один GC, который не будет консервативным?Такой сборщик мусора, как вы уже нашли, sgen.Я думаю, что целью sgen, помимо того, что он является точным сборщиком, является тот факт, что он уплотняет, что очень важно для долго работающих приложений, и что у него (иногда намного) лучшая производительность.Однако sgen все еще находится в стадии бета-тестирования, здесь и там можно наблюдать сбои.Кроме того, функция точного сканирования стека была включена и выключена в различных версиях Mono, иногда проходила некоторая регрессия, поэтому вы можете обнаружить, что более старая версия Mono работает лучше, чем более новая.Тем не менее, sgen активно развивается (как можно найти историю коммитов на github) и вскоре должен занять место сборщика мусора по умолчанию в Mono.Что в конечном итоге должно решить проблемы, подобные описанной.

Кстати, например, моя версия Mono (все еще 32-битная) проходит этот тест с sgen:

$ mono --gc=sgen GCTest.exe 
Memory: 4098 KB
Memory: 4140 KB
Memory: 1052716 KB

Надеюсь, это было полезно, спросите, есличто-то было неясно (особенно из-за моего второго качественного английского).

Редактировать:

На моей 64-битной машине Boehm работает хорошо:

$ mono GCTest.exe
Memory: 132 KB
Memory: 280 KB
Memory: 1048860 KB

(естественно тоже сген).Это Mono 2.10.5 в Linux.

...