Подтвердить что ты не робот

Сбор мусора в Моно

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

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

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

StackTrace:

at (wrapper managed-to-native) string.InternalAllocateStr(int) < 0xffffffff > в строке. Конкат (строка, строка) < 0x0005b > в GCtest.Main(строка []) < 0x00243 > at (завершение выполнения оболочки).runtime_invoke_void_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 _sigtramp + 43 3

0xffffffff 0x0 + 4294967295 4 mono-sgen
0x0020306d mono_gc_alloc_obj_nolock + 363 5 mono-sgen
0x0020394a mono_gc_alloc_string + 153 6 mono-sgen
0x001c9a10 mono_string_new_size + 147 7 mono-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 mono-sgen
0x001c7f92 mono_runtime_exec_main + 669 15 mono-sgen
0x001c72cc mono_runtime_run_main + 843 16 mono-sgen
0x0008c617 mono_main + 8551 17 mono-sgen
0x00002606 start + 54 18??? 0x00000003 0x0 + 3

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

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

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

/Users/fraser/Documents/diff-match-patch/csharp/GCtest.command: line 12: 41011 Ловушка прерывания: 6 mono -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");
  }
}
4b9b3361

Ответ 1

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

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

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

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

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

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

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

Edit:

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

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

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