2011-11-02 7 views
5

Creo que he rastreado una pérdida de memoria y quiero confirmar lo que creo que puede ser cierto acerca de cómo se implementa Binder de Android. En este caso, tengo un Servicio y una Actividad, cada uno en su propio proceso. Creé un AIDL que me permite pasar un objeto de devolución de llamada de la actividad al servicio a través de un método ipc y luego llamar cuando se realiza el servicio con la tarea solicitada.Carpeta de prevención de la recolección de basura

Durante mucho tiempo me he estado preguntando: si paso un nuevo objeto de devolución de llamada al servicio y no guardo un puntero al objeto de devolución de llamada en mi actividad ¿por qué el recolector de basura no sigue adelante y recoge la devolución de llamada en mi proceso de actividad? Dado que eso no parece suceder, ¿cómo sabe la JVM cuándo recolectar basura la devolución de llamada en mi actividad?

Creo que la respuesta es que el sistema Binder mantiene un puntero a mi devolución de llamada en el proceso de actividad hasta que el objeto callback correspondiente en el proceso de servicio tiene su método finalize() llamado, que envía un mensaje a la actividad para liberar el puntero. ¿Es esto correcto? Si no, ¿cómo funciona?

Creo que es así y conduce a una situación interesante en la que si la Devolución de llamada en la actividad apunta hacia algo con mucha memoria no se recogerá hasta que se recupere la devolución de llamada en el servicio. Si el servicio no tiene poca memoria, es posible que no recopile la devolución de llamada durante un tiempo prolongado y las devoluciones de llamada se acumularán en la actividad hasta que exista un OutOfMemoryError en la actividad.

Respuesta

6

Yury es bastante correcto.

Mi servicio inicia un hilo que contiene la devolución de llamada y cuando el hilo termina con su trabajo, llama a la devolución de llamada y el hilo finaliza. Cuando se llama a la devolución de llamada, puede hacer un poco de trabajo en mi actividad y luego regresar a ese punto, no tengo punteros en mi proceso de actividad para la devolución de llamada.

Sin embargo, el objeto de devolución de llamada en la actividad seguirá siendo señalado por el sistema de enlace de Android hasta que el objeto de devolución de llamada correspondiente en el servicio sea basura.

Si el objeto de devolución de llamada en el proceso de actividad domina algunos otros objetos que consumen mucha memoria, entonces estoy desperdiciando memoria en mi proceso de actividad sin una buena razón e incluso podría obtener un OutOfMemoryError. La solución es crear un método simple en mi clase de devolución de llamada llamada destory() para anular todos los campos de la devolución de llamada y llamar a ese método cuando haya terminado la devolución de llamada.

Si la clase de devolución de llamada es una clase interna no estática, puede considerar cambiarla a una clase interna estática y pasar la clase padre en el constructor, de esta manera puede anularla también en el destory() método.

Esto plantea una idea interesante, si la clase principal de una clase callback interna no estática es una actividad y ocurre un cambio de configuración (como una rotación de pantalla) después de que la devolución de llamada se envía a través del cuaderno pero antes llamado en ese momento, la devolución de llamada apunta a un objeto de actividad anterior cuando se ejecuta.

Actualización: Descubrí este código dentro de Binder.java, por supuesto está deshabilitado, pero hubiera sido bueno si hubieran mencionado este tipo de cosas en los Javadocs.

if (FIND_POTENTIAL_LEAKS) { 
     final Class<? extends Binder> klass = getClass(); 
     if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
       (klass.getModifiers() & Modifier.STATIC) == 0) { 
      Log.w(TAG, "The following Binder class should be static or leaks might occur: " + 
       klass.getCanonicalName()); 
     } 
    } 
1

Si entiendo correctamente cómo funciona Binder, el problema en su caso es el siguiente. Para cada llamada entrante, su Servicio crea un hilo separado. Cuando pasa un objeto a este hilo, su sistema Binder crea una copia local de su objeto para el hilo. Por lo tanto, hasta que su método de Servicio haya resultado, el hilo con la copia del objeto continúa funcionando.

Para comprobar esto simplemente intente ver los hilos de su proceso de Servicio (en DDMS).

Cuestiones relacionadas