长话短说:我可以分配大量的非托管内存,但是尝试在托管内存中分配相同数量(或更少)的内存会导致MonoTouch在GC_remap中崩溃(下面是调用堆栈)。
细节:
我将谈论上述行为的一个例子。我的应用程序偶尔会分配2.5MB的托管内存(使用new byte[]),并且它经常在我的iPhone4上死机,并出现下面粘贴的调用堆栈(即在分配期间出现mprotect错误)。我不会在单个函数调用之外保留对这些2.5MB块的引用。
MonoTouch团队表示,“mprotect errno 12”表示您已经耗尽了设备上的内存, 但问题是,我有大量可用于我的应用程序的内存。我可以在应用程序启动时分配0MB、10MB或200MB的非托管内存(使用Marshal.AllocHGlobal),每帧都触摸它,并且这对我的应用程序行为或此mprotect错误的频率没有任何影响。
短期内,我唯一想到的解决方法是在启动时分配一个大块内存(2-5MB),将其固定,使垃圾收集器永远不会触及它,并编写自己的分配器来按需分配此内存块的部分给我的应用程序。但如果这是在MonoTouch下可能实现的最佳解决方案,那么我将尽快从MonoTouch中脱身并要求退款。
一些注意事项
- GC.TotalMemory告诉我,我的应用程序始终占用 3MB 至 5MB 的托管内存使用量。
- 我在应用程序中有其他地方分配了更大的非托管内存块,但它从未在那里崩溃。我创建了压力测试,加载4MB的(非托管)纹理数据,将其交给GL,并每帧绘制它,直到我开始请求大块托管内存为止,应用程序是非常稳定的。
- 除非我自己调用GC.Collect,否则GC.CollectionCount几乎不会改变。
- 相同的行为发生在MonoTouch 3.2.3和MonoTouch 4.0上。
- 相同的行为在所有测试设备上都发生(iPhone 3G、3GS、4、iPad、iPad2)。
- 相同的行为在发布版本和调试版本中发生,尽管在调试版本中更频繁。
引起崩溃的方法
- 如果我创建一个循环调用GC.Collect然后睡眠1毫秒的线程,这会使崩溃更快地发生(例如,如果我在调试构建中,则几乎立即发生)。
- 使用某些.NET功能,如WebRequest,也会导致此崩溃。我只能假设它在其中某个地方分配了大块托管内存。
解决崩溃的方法
有两种方法可以降低崩溃频率或完全修复它:
- 如果我先预分配那个2.5MB的托管内存块,然后让它在应用程序的整个生命周期中保持不变,则不会出现崩溃。
- 如果在对其进行任何操作之前钉住这个2.5MB的内存块,似乎有所帮助。
结论/问题
由于这个问题,我们的应用程序尚未实现完全稳定。这个崩溃(总是在GC_remap内部)会在我们应用程序的随机分配中发生(我在这里提供的2.5MB示例只是我选择隔离和重现这个问题的一个示例)。
问题:
- 我能完全不信任托管分配器吗?
- 为什么我可以分配200MB的非托管内存,但是当我请求2.5MB时,托管分配器就会崩溃?(注意:即使我没有分配200MB的非托管内存,当我请求2.5MB时,它也会崩溃)。
- 为什么如果我在应用程序的生命周期中一直占用这2.5MB,那么应用程序就完全正常,但是如果我将其归还给系统(并调用GC.Collect),然后稍后再请求另外的2.5MB,崩溃情况会更糟糕!如果这真的是低内存条件,那么将2.5MB归还给系统而不是占用它是否更好?
我们能使用MonoTouch吗?
我的团队正在认真考虑放弃MonoTouch作为我们产品的开发工具,因为我们无法使其保持可靠稳定。
我们无法从MonoTouch团队那里得到任何关于这个问题的回复,无论是在stackoverflow上,还是通过在Novell网站上提交错误报告,或者直接给MonoTouch的支持邮件发送电子邮件。我们已经将我们的(托管和非托管)内存使用降到了荒谬的低水平,但由于这个问题,应用程序仍然会崩溃。短期内,我唯一想到的解决方法是在启动时分配一个大块内存(2-5MB),将其固定,使垃圾收集器永远不会触及它,并编写自己的分配器来按需分配此内存块的部分给我的应用程序。但如果这是在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.
=================================================================