为什么我们的MonoTouch应用程序打破垃圾收集器? 这并不是内存不足
我们有一个简单的问题,但其原因很复杂。 我们是经验丰富的开发人员,并且对可能造成的原因做了大量的研究。 我们希望MonoTouch开发人员能够与我们合作,找出似乎是人们共同面临的问题,而且目前还没有解决scheme。 我们已经在这方面做了两个多星期的工作,而且还没有能够解决这个问题。
问题是:为什么我们的MonoTouch应用程序打破垃圾收集器? 这并不是内存不足。
情况是,我们有一个应用程序,定期检查Web服务(可能每隔5秒)。 一段时间后,内存pipe理中止将失败。 这通常发生在大约一个半小时之后,但可以从十分钟到一夜之间。 在我们所有的testing设备上都会发生这种情况(我们总共有7个testing设备,包括iOS3和iOS4,iPod Touch,iPhones和iPad(1&2)。在StackOverflow上查看之后,我们在定时器中添加了一个System.Gc.Collect,这个改进稍微有些改进(失败需要更长的时间),但是并没有消失,值得一提的是,iPad的内存日志显示有777个空闲块,我们的应用程序使用了2041个空闲块,总共有26488个有线页面,由于我们已经收集了垃圾,并且没有做与5秒钟之前做的不一样的事情,所以内存不足似乎很奇怪。
我们升级到MonoTouch 4.0.1,但还没有解决。
StackOverflow可能是在同一问题,但不回答它的问题: 5666905/4545383/5492469/5426733
在iPad2上的故障堆栈如下。 失败可能发生在主线程或一个http线程,但总是在这个GC_序列中。 我已经在下面的内存pipe理器GC_remap的代码中进行了讨论。
线程10崩溃: 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
这是来自https://github.com/mono/mono/blob/master/libgc/os_dep.c的似乎是失败点的GC_remap代码
#ifdef NACL { / *氯化钠不暴露mprotect,但mmap应该工作正常* / 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 * /); 如果(mmap_result!=(void *)start_addr)ABORT(“mmap as mprotect failed”); / *假如mprotect成功,返回值。 * / 结果= 0; } #else / * NACL * / 结果= mprotect(start_addr,len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC); #endif / * NACL * / if(result!= 0){ GC_err_printf3( “Mprotect在0x%lx(长度为%ld)时失败,错误为%ld \ n”, start_addr,len,errno); ABORT(“Mprotect remapping failed”); } GC_unmapped_bytes - = len;
看起来ABORT是由于保护function失效造成的。 我们一直无法获得失败代码,因为问题不能在模拟器上显示。 mprotect函数似乎只是将内存标记为可读取/写入/执行。 内存pipe理器如何传递使其失败的参数? 它可能是通过一个不正确的指针,或不正确的长度? 或者某些地区或边界在iOS上的处理方式有所不同?
对于GC_allochblk_nth, https: //github.com/mono/mono/blob/master/libgc/allchblk.c中的代码暗示只有在find的内存块有效时才调用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阅读以外没有其他任何事情。 我们可以做什么来改善debugging(我们不能跟踪任何应用程序被iOS杀死)。 我们试图创build一个更简单的演示,但它不会失败到足以值得使用。 如果Novell MonoTouch开发者需要我们的资源,我们可以提供明显的机密性。
由于您的复制,我们发现并纠正了垃圾收集器中一个非常模糊的问题。 它将包含在MonoTouch 4.0.2中。
你问:
“内部内核结构不能被分配……这怎么可能?
一个原因可能是内存碎片 ,有关此主题的更多信息,请参阅示例如何解决内存碎片 。 这也可能属于文件指针等其他内部资源。 因此,问题只在资源有限的设备上可见,而不在模拟器中。
要find非明显的资源泄漏:您是否正在使用Visual Studio的静态代码分析 (FxCop)? 一些规则提供了一些提示来明确地释放资源,例如调用Dispose或者build议实现IDispose接口。