Нестабильность MonoTouch продолжается: происходит сбой управляемого распределителя памяти - PullRequest
8 голосов
/ 14 апреля 2011

Короче говоря: Я могу выделить тонны неуправляемой памяти, но при выделении такого же количества (или намного меньше) в управляемой памяти происходит сбой MonoTouch в GC_remap (callstack ниже).

Детали:

Я расскажу об одном примере поведения, который я описал выше. Мое приложение время от времени выделяет 2,5 МБ управляемой памяти (используя новый байт []), и оно часто умирает на моем iPhone4 со вставленным ниже колл-стеком (то есть ошибка mprotect во время выделения). Я не храню ссылку на эти блоки размером 2,5 МБ дольше, чем один вызов функции.

Ребята из MonoTouch говорят, что «mprotect errno 12» означает, что вы исчерпали память на своем устройстве , но дело в том, что для моего приложения доступно много памяти. Я могу выделить 0MB , 10MB или 200MB из неуправляемой памяти (используя Marshal.AllocHGlobal) при запуске моего приложения, прикасаться к ней каждый раз кадров, и это делает нулевую разницу в поведении моего приложения или в частоте этой ошибки mprotect.

Некоторые заметки

  • GC.TotalMemory сообщает мне, что мое приложение постоянно находится между 3 МБ и 5 МБ управляемой памяти.
  • В моем приложении есть другие места, где я выделяю еще большие блоки неуправляемой памяти, и она там никогда не падает. Я создал стресс-тесты, которые загружают 4 МБ (неуправляемых) текстурных данных, передают их в GL, и рисуют каждый кадр, и приложение работает отлично, пока я не начну запрашивать большие блоки управляемой памяти.
  • GC.CollectionCount почти никогда не изменится, если я не вызову GC.Collect себя.
  • То же происходит и с MonoTouch 3.2.3, а также с MonoTouch 4.0.
  • Такое же поведение наблюдается на всех наших тестирующих устройствах (iPhone 3G, 3GS, 4, iPad, iPad2).
  • То же самое происходит в сборках релизов и отладочных сборках, хотя чаще это происходит в отладочных сборках.

Способы спровоцировать аварию

  • Если я создаю поток, который зацикливается на вызове GC.Collect, а затем спит в течение 1 мс, это приводит к аварийному завершению намного раньше (т. Е. Практически сразу, если я нахожусь в отладочной сборке).
  • Использование некоторых функций .NET, таких как WebRequest, также приведет к сбою. Я могу только предположить, что он также выделяет большие блоки управляемой памяти где-то там.

Пути крушения

Существует два способа уменьшить частоту сбоев или исправить ее:

  1. Если я предварительно выделю этот 2,5 МБ блок управляемой памяти, а затем просто сохраню его в течение всего срока службы приложения, то сбоев не будет.
  2. Если я закреплю кусок памяти объемом 2,5 МБ, прежде чем что-то с ним делать, это, похоже, поможет.

Выводы / вопросы

Из-за этой проблемы мы еще не достигли полной стабильности в нашем приложении. Этот сбой (всегда внутри GC_remap) происходит при произвольном распределении по всему нашему приложению (у меня есть пример размером 2,5 МБ, который я выбрал для изоляции и воспроизведения).

Вопросы:

  1. Могу ли я вообще не доверять управляемому распределителю?
  2. Почему я могу выделить 200 МБ неуправляемой памяти, но управляемый распределитель умирает, когда я запрашиваю 2,5 МБ ? (Примечание: он умрет, когда я запрашиваю 2,5 МБ, даже если я не выделил 200 МБ неуправляемой памяти).
  3. Почему с приложением все в порядке, если я использую эти 2,5 МБ на протяжении всего жизненного цикла приложения, но если я возвращаю их системе (и вызываю GC.Collect), а потом запрашиваю еще 2,5 МБ, крушение хуже! Если это действительно нехватка памяти, не лучше ли вернуть системе 2,5 МБ, чем перегружать ее?

Можем ли мы даже использовать MonoTouch?

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

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

В краткосрочной перспективе единственный обходной путь, который я имею в виду, - это выделить большой кусок памяти (2-5 МБ) при запуске, ввести PIN-код, чтобы сборщик мусора никогда не касался этого, и написать свой собственный.распределитель для добавления частей этого блока памяти в мое приложение по мере необходимости.Но если это лучшее решение, которое возможно при MonoTouch, я собираюсь вернуть свои деньги, как только смогу достичь скорости выхода из MonoTouch.

...

Mprotect failed at 0xaa00000 (length 3801088) with errno 12
Stacktrace:

  at MyApp.GameScreen/VerifyPictureDialog.StoreBasePictureData () [0x00000] in /Users/dussault/s/MyApp/Main/Src/PhotoScreens.cs:428
  at MyApp.GameScreen/VerifyPictureDialog.ApplyFilters (bool) [0x0004b] in /Users/dussault/s/MyApp/Main/Src/PhotoScreens.cs:640
  at MyApp.GameScreen/VerifyPictureDialog.Simulate (single) [0x00077] in /Users/dussault/s/MyApp/Main/Src/PhotoScreens.cs:477
  at MyApp.BaseWindow.Simulate (single) [0x00007] in /Users/dussault/s/MyApp/Main/Src/BaseWindow.cs:56
  at MyApp.BaseWindow.Simulate (single) [0x00007] in /Users/dussault/s/MyApp/Main/Src/BaseWindow.cs:56
  at MyApp.GameScreen.Simulate (single) [0x00238] in /Users/dussault/s/MyApp/Main/Src/GameScreen.cs:3114
  at MyApp.BaseWindow.Simulate (single) [0x00007] in /Users/dussault/s/MyApp/Main/Src/BaseWindow.cs:56
  at MyApp.WindowMgr.Simulate (single) [0x0002f] in /Users/dussault/s/MyApp/Main/Src/WindowMgr.cs:126
  at MyApp.Game1.Update (Microsoft.Xna.Framework.GameTime) [0x0010f] in /Users/dussault/s/MyApp/Main/Src/Game1.cs:1194
  at Microsoft.Xna.Framework.Game.DispatchUpdate (Microsoft.Xna.Framework.GameTime) [0x00000] in /Users/dussault/s/MyApp/Main/Src/XNA-Emulation/GraphicsDevice.cs:531
  at MyApp_iOS.EAGLView.OnUpdateFrame () [0x00050] in /Users/dussault/s/MyApp/Main/Src/iOS/EAGLView.cs:310
  at MyApp_iOS.EAGLView.SimulateAndRender () [0x0000a] in /Users/dussault/s/MyApp/Main/Src/iOS/EAGLView.cs:279
  at MyApp_iOS.EAGLView.MainLoopTimerCallback () [0x00006] in /Users/dussault/s/MyApp/Main/Src/iOS/EAGLView.cs:231
  at MonoTouch.Foundation.NSActionDispatcher.Apply () <0x0002b>
  at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>
  at MonoTouch.UIKit.UIApplication.Main (string[],string,string) <0x000cf>
  at MonoTouch.UIKit.UIApplication.Main (string[]) <0x00023>
  at MyApp_iOS.Application.Main (string[]) [0x00000] in /Users/dussault/s/MyApp/Main/Src/iOS/Main.cs:57
  at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>

Native stacktrace:

    0   MyApp_iOS                           0x00af1b48 mono_handle_native_sigsegv + 412
    1   MyApp_iOS                           0x00b1c66c sigabrt_signal_handler + 148
    2   libSystem.B.dylib                   0x33bd3ddf _sigtramp + 42
    3   libSystem.B.dylib                   0x33bd52cb kill + 10
    4   libSystem.B.dylib                   0x33bd52bd raise + 16
    5   libSystem.B.dylib                   0x33be9d79 abort + 56
    6   MyApp_iOS                           0x00c74378 GC_remap + 200
    7   MyApp_iOS                           0x00c62c04 GC_allochblk_nth + 1536
    8   MyApp_iOS                           0x00c625b4 GC_allochblk + 96
    9   MyApp_iOS                           0x00c6bf6c GC_alloc_large + 132
    10  MyApp_iOS                           0x00c6c5e8 GC_generic_malloc + 324
    11  MyApp_iOS                           0x00c6c8c8 GC_malloc_atomic + 332
    12  MyApp_iOS                           0x00bd8e88 mono_object_allocate_ptrfree + 64
    13  MyApp_iOS                           0x00bd8ff4 mono_array_new_specific + 148
    14  MyApp_iOS                           0x009173f4 wrapper_managed_to_native_object___icall_wrapper_mono_array_new_specific_intptr_int + 68
    15  MyApp_iOS                           0x002cd880 MyApp_GameScreen_VerifyPictureDialog_ApplyFilters_bool + 628
    16  MyApp_iOS                           0x002cbffc MyApp_GameScreen_VerifyPictureDialog_Simulate_single + 768
    17  MyApp_iOS                           0x002ef9d0 MyApp_BaseWindow_Simulate_single + 280
    18  MyApp_iOS                           0x002ef9d0 MyApp_BaseWindow_Simulate_single + 280
    19  MyApp_iOS                           0x002a71fc MyApp_GameScreen_Simulate_single + 2736
    20  MyApp_iOS                           0x002ef9d0 MyApp_BaseWindow_Simulate_single + 280
    21  MyApp_iOS                           0x0038068c MyApp_WindowMgr_Simulate_single + 376
    22  MyApp_iOS                           0x0027f798 MyApp_Game1_Update_Microsoft_Xna_Framework_GameTime + 1992
    23  MyApp_iOS                           0x0039afc8 Microsoft_Xna_Framework_Game_DispatchUpdate_Microsoft_Xna_Framework_GameTime + 148
    24  MyApp_iOS                           0x0026ec10 MyApp_iOS_EAGLView_OnUpdateFrame + 716
    25  MyApp_iOS                           0x0026e8cc MyApp_iOS_EAGLView_SimulateAndRender + 196
    26  MyApp_iOS                           0x0026e1cc MyApp_iOS_EAGLView_MainLoopTimerCallback + 296
    27  MyApp_iOS                           0x009a7dfc MonoTouch_Foundation_NSActionDispatcher_Apply + 44
    28  MyApp_iOS                           0x00912540 wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 200
    29  MyApp_iOS                           0x00acc9c4 mono_jit_runtime_invoke + 2800
    30  MyApp_iOS                           0x00bd3ea4 mono_runtime_invoke + 140
    31  MyApp_iOS                           0x00c7d214 monotouch_trampoline + 2840
    32  Foundation                          0x3363b469 __NSFireTimer + 136
    33  CoreFoundation                      0x33a770a3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 14
    34  CoreFoundation                      0x33a76b5b __CFRunLoopDoTimer + 850
    35  CoreFoundation                      0x33a481b5 __CFRunLoopRun + 1088
    36  CoreFoundation                      0x33a47c87 CFRunLoopRunSpecific + 230
    37  CoreFoundation                      0x33a47b8f CFRunLoopRunInMode + 58
    38  GraphicsServices                    0x33b0e4ab GSEventRunModal + 114
    39  GraphicsServices                    0x33b0e557 GSEventRun + 62
    40  UIKit                               0x32099329 -[UIApplication _run] + 412
    41  UIKit                               0x32096e93 UIApplicationMain + 670
    42  MyApp_iOS                           0x009d484c wrapper_managed_to_native_MonoTouch_UIKit_UIApplication_UIApplicationMain_int_string___intptr_intptr + 240
    43  MyApp_iOS                           0x009b4c00 MonoTouch_UIKit_UIApplication_Main_string__ + 36
    44  MyApp_iOS                           0x00269694 MyApp_iOS_Application_Main_string__ + 128
    45  MyApp_iOS                           0x00912540 wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 200
    46  MyApp_iOS                           0x00acc9c4 mono_jit_runtime_invoke + 2800
    47  MyApp_iOS                           0x00bd3ea4 mono_runtime_invoke + 140
    48  MyApp_iOS                           0x00bd6f3c mono_runtime_exec_main + 784
    49  MyApp_iOS                           0x00bd5f6c mono_runtime_run_main + 1048
    50  MyApp_iOS                           0x00ad7940 mono_jit_exec + 216
    51  MyApp_iOS                           0x00ac2e38 main + 3536
    52  MyApp_iOS                           0x000133a0 start + 52

Debug info from gdb:


=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================

1 Ответ

1 голос
/ 15 апреля 2011

Ядро Дарвина перегружает память.

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

Только когда вы действительно запишете страницу, страница будет выделена вашему процессу.

Для правильного теста вам потребуется выделить память и затем заполнить использованную памятьВот почему у вас, вероятно, складывается впечатление, что вы можете выделить 200 мегабайт памяти, даже если у вас его даже нет.

Простая программа покажет вам это: попробуйте выделить 500 мегабайт, операционная система скажет:Да, вы получили это ", но нет айфонов с таким типом памяти.

Пример тестового примера дал бы долгий путь, чтобы показать, в чем проблема.

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