为什么我们的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接口。