¿Por qué nuestra aplicación MonoTouch se está rompiendo en el recolector de basura? No está fuera de la memoria

Tenemos una pregunta simple, pero la causa es complicada. Somos desarrolladores experimentados y hemos investigado mucho sobre lo que puede estar causando. Esperamos que los desarrolladores de MonoTouch puedan trabajar con nosotros para identificar lo que parece ser un problema común que las personas están teniendo y para el cual todavía no parece existir ninguna solución. Hemos estado trabajando en esto durante más de dos semanas y no hemos podido resolverlo.

La pregunta es: ¿por qué nuestra aplicación MonoTouch se está rompiendo en el recolector de basura? No está fuera de la memoria.

La situación es que tenemos una aplicación que revisa un servicio web regularmente (quizás cada 5 segundos). Después de un período de tiempo falla con un aborto de administración de memoria. Esto generalmente ocurre después de aproximadamente una hora y media, pero puede ser de diez minutos a la noche. Esto sucede en todos nuestros dispositivos de prueba (tenemos 7 en total que cubren iOS3 e iOS4, iPod Touch, iPhones y iPads (1 y 2). Después de buscar en StackOverflow, hemos agregado un System.Gc.Collect en un temporizador antes de tomar cualquier acción. Esto mejoró un poco (tarda más en fallar), pero no desapareció. También vale la pena agregar que el registro de memoria del iPad muestra que hay 777 bloques libres y 2041 en uso en nuestra aplicación. con un total de 26488 páginas con cable. Como hemos recogido basura, y no estamos haciendo nada diferente a lo que hicimos 5 segundos antes, parece extraño quedarse sin memoria.

Actualizamos a MonoTouch 4.0.1 pero eso no lo ha solucionado.

Preguntas de StackOverflow que pueden estar relacionadas con el mismo problema, pero que no lo responden: 5666905/4545383/5492469/5426733

La stack en la falla en un iPad2 está debajo. La falla puede ocurrir en el hilo principal o en el hilo http, pero siempre va en esta secuencia GC_. He incluido el código para el administrador de memoria GC_remap a continuación, con discusión.

  Thread 10 Crashed:
 0 libsystem_kernel.dylib 0x34b4da1c __pthread_kill + 8
 1 libsystem_c.dylib 0x3646a3b4 pthread_kill + 52
 2 libsystem_c.dylib 0x36462bf8 abortar + 72
 3 MyApp 0x004ca92c mono_handle_native_sigsegv (mini-excepciones.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 abortar + 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 (hilos.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

Este es el código GC_remap que parece ser el punto de falla, de https://github.com/mono/mono/blob/master/libgc/os_dep.c

 #ifdef NACL
       {
     / * NaCl no expone mprotect, pero mmap debería funcionar bien * /
     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 como mprotect failed");
         / * Fake el valor de retorno como si mprotect tuviera éxito.  * /
         resultado = 0;
       }
 #else / * NACL * /
       resultado = mprotect (start_addr, len,
                 PROT_READ |  PROT_WRITE |  OPT_PROT_EXEC);
 #endif / * NACL * /
       if (resultado! = 0) {
       GC_err_printf3 (
         "Mprotect falló en 0x% lx (longitud% ld) con errno% ld \ n",
             start_addr, len, errno);
       ABORT ("reaspepe Mprotect falló");
       }
       GC_unmapped_bytes - = len;

Parece que el ABORTO es causado por la falla de la función mprotect. No hemos podido obtener el código de falla ya que el problema no se manifiesta en el simulador. La función mprotect parece simplemente marcar la memoria como accesible para lectura / escritura / ejecución. ¿Cómo pasa el administrador de memoria los parámetros que hacen que falle? ¿Podría estar pasando un puntero incorrecto o una longitud incorrecta? ¿O ciertas áreas o límites se manejan de manera diferente en iOS?

El código en https://github.com/mono/mono/blob/master/libgc/allchblk.c para GC_allochblk_nth implica que la función GC_remap solo se invoca si el bloque de memoria encontrado era válido. (Este archivo no coincide exactamente con los números de línea del seguimiento de stack, por lo que presumiblemente no es exactamente el mismo archivo).

http://developer.apple.com/library/ios/#documentation/System/Conceptual/ManPages_iPhoneOS/man2/mprotect.2.html dice que podría fallar con EACCES, EINVAL, ENOTSUP que son 13, 22 y 45 respectivamente . Uno de los informes sobre SO dice que reciben un error 12 (ENOMEM). No estoy seguro de lo que eso significa, ya que mprotect no debe asignar memoria, y la documentación no dice que eso sea válido.

Una documentación más genérica en http://linux.die.net/man/2/mprotect indica que ENOMEM puede ser causado por “Las estructuras internas del núcleo no se pudieron asignar. O bien: las direcciones en el rango [addr, addr + len] son no válido para el espacio de direcciones del proceso, o especifique una o más páginas que no están mapeadas. ” ¿Cómo podría ser esto?

Agradeceríamos cualquier sugerencia sobre cómo podemos avanzar. No estamos haciendo nada más que el código C #, y no estamos haciendo otra cosa que una lectura https periódica. ¿Qué podemos hacer para mejorar la depuración (no podemos rastrear nada ya que iOS mata la aplicación). Hemos intentado crear una demostración más simple, pero no falla lo suficientemente rápido para que valga la pena usarla. Si un desarrollador de Novell MonoTouch desea nuestra fuente, podemos proporcionarla sujeta a la confidencialidad obvia.

Gracias a su reproducción, hemos encontrado y corregido un problema muy poco conocido en el recolector de basura. Se incluirá en MonoTouch 4.0.2.

Tu preguntaste:

“Las estructuras internas del núcleo no se pudieron asignar … ¿Cómo podría ser esto?

Una razón podría ser la fragmentación de la memoria, para obtener más información sobre este tema, consulte, por ejemplo, Cómo resolver la fragmentación de la memoria . Esto también puede pertenecer a otros recursos internos, como punteros de archivo. Por lo tanto, el problema solo es visible en los dispositivos con recursos limitados y no en el simulador.

Para encontrar fugas de recursos no obvias también: ¿Está utilizando el Análisis de código estático (FxCop) de Visual Studio? Algunas de las reglas dan pistas sobre los recursos gratuitos de forma explícita, por ejemplo, llamar a Dispose o sugiere implementar la interfaz IDispose.