2009-09-05 11 views
13

he estado jugando un poco con diferentes compilaciones de mi aplicación y parece que suceden cosas extrañas:cocoa ¿Los binarios de 64 bits pierden memoria? (liberar NSData no libera la memoria)

mi aplicación tiene una huella de inactividad de 5mb. al cargar una memoria de archivo en el tamaño del archivo está reservado. después de la carga, la memoria reservada debe liberarse. ahora hay diferencias en las construcciones (gc = recolector de basura):

  • 32bit i386 no-GC: toda la memoria se libera al instante.
  • 32bit i386 GC: casi toda la memoria se libera al instante. el resto un tiempo después.
  • 64bit x86_64 no-GC: la memoria mínima se libera. como 10%
  • 64bit x86_64 GC: no se libera ninguna memoria. la memoria permanece reservada durante horas. (actividad mon)

uso LLVM con CLANG. He estado corriendo instrumentos hoy todo el tiempo y estaba buscando fugas/zombies/etc. y todo parece estar limpio. (la aplicación es bastante simple.)

¿Hay una explicación para este comportamiento?


Actualización:

Eso es un poco de materia extraña. He hervido el problema con esto:

Cargué un archivo de 20 mb en un NSData y lo liberé. Estoy haciendo esto sin ninguna recolección de basura habilitada. El código es:

NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigshit"]; 
[bla release]; 

Cuando construyo para i386 32 bits, los 20mb se asignan y liberan. Cuando cambio la compilación a 64bit x86_64, la versión no hace nada. La estancia de 20 mb asignada.

upper pic 32bit lower 64 http://kttns.org/zguxn

No hay diferencia entre las dos aplicaciones, excepto que el superior está construida de 32 bits y el inferior de 64 bits. No hay GC funcionando. (Con GC activado aparece el mismo problema.)


Actualización 2:

El mismo comportamiento se observa cuando se crea una nueva aplicación de cacao desde cero con sólo el código superior en applicationDidFinishLaunching :. En el modo de 64 bits, la memoria no se libera. i386 funciona como se esperaba.

El mismo problema aparece con NSString en lugar de NSData. También aparece cuando reinicio el kernel de 64 bits. (64 Sosteniendo al inicio.)

OS 10.6.0 es

+0

¿Ha intentado asignar y desasignar muchas instancias de NSData para ver si la memoria porque alguno de ellos es reclamado? Es posible que el programa no devuelva la memoria al sistema operativo a menos que haya una escasez de memoria. – Amok

+0

Acabo de intentar reproducir esto en 10.6 sin suerte. ¿Ha archivado un error con su aplicación de prueba? – Ken

+0

Su aplicación se comporta correctamente y no tiene pérdidas de memoria. Vea mi respuesta a continuación para una explicación semi detallada. – bbum

Respuesta

10

Primero, use el instrumento de Objeto Gráfico del Instrumento para verificar que la memoria ya no se considere en uso; no tiene un conteo retenido o una referencia fuerte en alguna parte.

Si ya no está en uso, la memoria se está quedando simplemente porque no ha alcanzado el umbral al que le importa el colector.

Sin embargo, esta declaración:

64 bits x86_64 no-GC: mínimo de memoria es liberado. como 10%

Me hace recelar. Específicamente, si su código está diseñado para funcionar en sistemas que no son GC, con retención/liberación, entonces (a) tiene una pérdida de memoria y, si utiliza CFRetain o algún tipo de caché global, eso podría afectar GC o (b) no está utilizando las herramientas adecuadas para averiguar si tiene o no una pérdida de memoria.

Entonces, ¿cómo está determinando que está perdiendo memoria?

Actualización; está utilizando el Monitor de actividad para monitorear la RSIZE/VSIZE del proceso. En realidad, esto no te dirá nada útil más allá de "mi proceso está creciendo con el tiempo".

más probable es que (no he mirado en la fuente), este código:

NSData *bla = [[NSData alloc] initWithContentsOfFile:@"/bigpoop"]; 

hará que el archivo de 20 MB para ser mmap() 'd en el proceso. No hay una asignación de estilo malloc() involucrada en absoluto. En cambio, el sistema operativo entrega 20 MB de espacio de direcciones contiguas a su proceso y asigna el contenido del archivo en él. A medida que lea el contenido de NSData, se publicará el error en el archivo a medida que avanza.

Cuando suelta bla, la asignación se destruye. Pero eso no significa que el subsistema VM vaya a reducir el espacio de direcciones de su aplicación en 20 MB.

Por lo tanto, está quemando un montón de espacio de direcciones, pero no la memoria real. Dado que su proceso es de 64 bits, el espacio de direcciones es prácticamente un recurso infinito y la utilización de direcciones tiene un costo muy bajo, por lo que el sistema operativo se implementa de esta manera.

I.e. no hay fugas y su aplicación se comporta correctamente, GC o no.

Esto es una idea falsa común y, por lo tanto, se inició la pregunta.

+0

Estoy usando los instrumentos de pérdida de memoria. Y como una construcción de 32 bits todo funciona como se esperaba. – jsz

+0

por favor lea mis actualizaciones a la pregunta original. – jsz

+0

gracias por la respuesta. parece que tienes razón :) – jsz

2

Un recolector de basura no necesariamente libera memoria inmediatamente.

En el caso del recolector de basura de Objective-C, puede enviar recolector de basura de cacao objeto un mensaje collectIfNeeded sugerir que ahora puede ser un buen momento para hacer un poco de colección, o collectExhaustively a la orden para iniciar de inmediato la recogida de todos y cada uno toda la basura (pero incluso esto es interrumpible). Ver the docs.

0

Tengo un problema muy similar en iPhoneOS 3.2 y realmente no creo que la memoria esté siendo reclamada. Eventualmente desencadenaré advertencias de memoria. Hay una pequeña posibilidad de que haya pasado por alto un error mío, pero he sido muy minucioso.

Utilizo unarchiveObjectWithFile de NSKeyedUnarchiver: para cargar un objeto personalizado que contiene un solo NSData grande y otro objeto mucho más pequeño. Se llama al método dealloc en mi objeto personalizado, se lanza el objeto NSData, su retenciónCount == 1 justo antes. La memoria física no disminuye en ninguna cantidad, mucho menos una fracción del tamaño NSData, y con la memoria de repetición las advertencias se generan de manera confiable: Tengo una prueba hasta que realmente recibí advertencias de nivel 2.= (

Antes de liberación:

(GDB) p (int) [(NSData *) pastItsWelcomeData retainCount]
$ 1 = 1

Después de la liberación:

(GDB) p (int) [(NSData *) pastItsWelcomeData retainCount]
El destino no responde a este selector de mensajes

+0

Tiene un problema diferente. GC no existe en el iPhone, y 'retainCount' es lo incorrecto que se debe usar para depurar problemas de toda la vida del objeto. (Use los instrumentos en su lugar). Dado que esta es una descripción de un problema diferente y no responde la pregunta, debe publicarlo como una pregunta separada. –

Cuestiones relacionadas