Почему наше приложение MonoTouch ломается в сборщике мусора? Это не из памяти - PullRequest
17 голосов
/ 28 апреля 2011

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

Вопрос в том, почему наше приложение MonoTouch ломается в сборщике мусора? Это не из памяти.

Ситуация такова, что у нас есть приложение, которое регулярно проверяет веб-сервис (возможно, каждые 5 секунд). Через некоторое время происходит сбой при прерывании управления памятью. Обычно это происходит примерно через полтора часа, но может длиться от десяти минут до ночи. Это происходит на всех наших тестовых устройствах (у нас их в общей сложности 7, охватывающих iOS3 и iOS4, iPod Touch, iPhone и iPad (1 и 2). После просмотра StackOverflow мы добавили System.Gc.Collect в таймере, прежде чем принимать какие-либо действие. Это немного улучшило ситуацию (требуется больше времени, чтобы потерпеть неудачу), но оно не исчезло. Стоит также добавить, что журнал памяти iPad показывает, что в нашем приложении 777 свободных блоков и 2041 используется, всего 26488 проводных страниц. Так как мы собрали мусор и не делаем ничего, отличного от того, что мы делали 5 секунд назад, кажется странным нехватка памяти.

Мы обновились до MonoTouch 4.0.1, но это не исправило.

вопросы StackOverflow, которые могут быть по той же проблеме, но не отвечать на нее: 5666905 / 4545383 / 5492469 / 5426733

Стек при сбое на iPad2 ниже. Ошибка может произойти в основном потоке или в http-потоке, но всегда происходит в этой последовательности GC_. Я включил код для менеджера памяти GC_remap ниже, с обсуждением.

Thread 10 Crashed:
0   libsystem_kernel.dylib  0x34b4da1c __pthread_kill + 8
1   libsystem_c.dylib       0x3646a3b4 pthread_kill + 52
2   libsystem_c.dylib       0x36462bf8 abort + 72
3   MyApp                   0x004ca92c mono_handle_native_sigsegv (mini-exceptions.c:2249)
4   MyApp                   0x004f2208 sigabrt_signal_handler (mini-posix.c:195)
5   libsystem_c.dylib       0x36475728 _sigtramp + 36
6   libsystem_c.dylib       0x3646a3b4 pthread_kill + 52
7   libsystem_c.dylib       0x36462bf8 abort + 72
8   MyApp                   0x0061dc94 GC_remap (os_dep.c:2092)
9   MyApp                   0x00611678 GC_allochblk_nth (allchblk.c:730)
10  MyApp                   0x00611028 GC_allochblk (allchblk.c:561)
11  MyApp                   0x0061d0e0 GC_new_hblk (new_hblk.c:253)
12  MyApp                   0x006133d0 GC_allocobj (alloc.c:1116)
13  MyApp                   0x00617d30 GC_generic_malloc_inner (malloc.c:136)
14  MyApp                   0x00617f40 GC_generic_malloc (malloc.c:192)
15  MyApp                   0x00618264 GC_malloc_atomic (malloc.c:262)
16  MyApp                   0x005a46d4 mono_object_allocate_ptrfree (object.c:4221)
17  MyApp                   0x005a4aa0 mono_string_new_size (object.c:4848)
18  MyApp                   0x005c1b14 ves_icall_System_String_InternalAllocateStr (string-icalls.c:213)
19  MyApp                   0x002d34c4 wrapper_managed_to_native_string_InternalAllocateStr_int + 52
20  MyApp                   0x002cff5c string_ToLower_System_Globalization_CultureInfo + 56
21  MyApp                   0x003e6ac0 System_Net_WebRequest_GetCreator_string + 40
22  MyApp                   0x003e694c System_Net_WebRequest_Create_System_Uri + 48
23  MyApp                   0x003e68d8 System_Net_WebRequest_Create_string + 64
24  MyApp                   0x004489c4 MyApp_Services_Client_GetResponseContent_string + 152
25  MyApp                   0x00446288 MyApp_Services_Client_GetCurrentQuestion_long_long + 916
26  MyApp                   0x00196fcc MyApp_Iphone_RootViewController_RetrieveCurrentQuestion + 868
27  MyApp                   0x002e6368 System_Threading_Thread_StartUnsafe + 168
28  MyApp                   0x00306890 wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 192
29  MyApp                   0x004b0274 mono_jit_runtime_invoke (mini.c:5746)
30  MyApp                   0x0059f924 mono_runtime_invoke (object.c:2756)
31  MyApp                   0x005a1350 mono_runtime_delegate_invoke (object.c:3421)
32  MyApp                   0x005ca884 start_wrapper_internal (threads.c:788)
33  MyApp                   0x005ca924 start_wrapper (threads.c:830)
34  MyApp                   0x005ef4b8 thread_start_routine (wthreads.c:285)
35  MyApp                   0x0061f1d0 GC_start_routine (pthread_support.c:1468)
36  libsystem_c.dylib       0x3646a30a _pthread_start + 242
37  libsystem_c.dylib       0x3646bbb4 thread_start + 0

Это код GC_remap, который, по-видимому, является точкой отказа, начиная с https://github.com/mono/mono/blob/master/libgc/os_dep.c

#ifdef NACL
      {
    /* NaCl doesn't expose mprotect, but mmap should work fine */
    void * mmap_result;
        mmap_result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC,
              MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON,
              zero_fd, 0/* offset */);
        if (mmap_result != (void *)start_addr) ABORT("mmap as mprotect failed");
        /* Fake the return value as if mprotect succeeded. */
        result = 0;
      }
#else /* NACL */
      result = mprotect(start_addr, len,
                PROT_READ | PROT_WRITE | OPT_PROT_EXEC);
#endif /* NACL */
      if (result != 0) {
      GC_err_printf3(
        "Mprotect failed at 0x%lx (length %ld) with errno %ld\n",
            start_addr, len, errno);
      ABORT("Mprotect remapping failed");
      }
      GC_unmapped_bytes -= len;

Может показаться, что ABORT вызван ошибкой функции mprotect. Нам не удалось получить код ошибки, поскольку проблема не проявляется на симуляторе. Функция mprotect, по-видимому, просто помечает память как доступную для чтения / записи / выполнения. Как диспетчер памяти передает параметры, которые вызывают его сбой? Это может быть неверный указатель или неправильная длина? Или определенные области или границы обрабатываются по-разному на iOS?

Код в https://github.com/mono/mono/blob/master/libgc/allchblk.c для GC_allochblk_nth подразумевает, что функция GC_remap вызывается только в том случае, если найденный блок памяти был действительным. (Этот файл не совсем соответствует номерам строк трассировки стека, поэтому, предположительно, это не совсем тот же файл.)

http://developer.apple.com/library/ios/#documentation/System/Conceptual/ManPages_iPhoneOS/man2/mprotect.2.html говорит, что может произойти сбой с EACCES, EINVAL, ENOTSUP, которые равны 13, 22 и 45 соответственно. В одном из отчетов о SO говорится, что они получают ошибку 12 (ENOMEM). Я не уверен, что это значит, поскольку mprotect не должен выделять память, а в документации не говорится, что это правильно.

Более общая документация в http://linux.die.net/man/2/mprotect указывает, что ENOMEM может быть вызван "Внутренние структуры ядра не могут быть выделены. Или: адреса в диапазоне [addr, addr + len] недопустимы для адресного пространства или укажите одну или несколько страниц, которые не отображаются. " Как это могло быть?

Мы были бы очень благодарны за любые предложения о том, как мы можем продвинуться вперед. Мы не делаем ничего, кроме кода C #, и не делаем ничего, кроме периодического чтения https. Что мы можем сделать для улучшения отладки (мы ничего не можем отследить, так как приложение убито iOS). Мы попытались создать более простую демонстрацию, но она не дает сбоя достаточно быстро, чтобы ее стоило использовать. Если разработчик Novell MonoTouch хочет получить наш источник, мы можем предоставить его при условии явной конфиденциальности.

Ответы [ 2 ]

13 голосов
/ 01 мая 2011

Благодаря вашему воспроизведению мы нашли и исправили очень неясную проблему в сборщике мусора.Он будет включен в MonoTouch 4.0.2.

0 голосов
/ 29 апреля 2011

Вы спросили:

"Внутренние структуры ядра не могут выделяться. ... Как это могло быть?

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

Чтобы найти неочевидные утечки ресурсов, также: Используете ли вы Статический анализ кода (FxCop) Visual Studio? Некоторые из правил явно указывают на освобождение ресурсов, например, вызвать Dispose или предлагает реализовать интерфейс IDispose.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...