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

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

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

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

Ситуация заключается в том, что у нас есть приложение, которое регулярно проверяет веб-сервис (возможно, каждые 5 секунд). По истечении определенного периода времени он прерывается с отменой управления памятью. Обычно это происходит примерно через полтора часа, но может быть от десяти минут до ночи. Это происходит на всех наших тестовых устройствах (у нас есть 7 в общем покрытии iOS3 и iOS4, iPod Touch, iPhones и iPads (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] недопустимы для адресного пространства процесса или укажите одну или несколько страниц, которые не отображаются." Как это могло быть?

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

4b9b3361

Ответ 1

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

Ответ 2

Вы спросили:

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

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

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