variable usar una sirve que programacion para metodos metodo estaticos estaticas estatica ejemplos cuando clases clase atributos java android garbage-collection singleton classloader

java - usar - Las referencias estáticas se borran: ¿Android descarga las clases en tiempo de ejecución si no se utiliza?



static programacion (4)

He visto un comportamiento extraño similar con mi propio código que involucra la desaparición de variables estáticas (no creo que este problema tenga nada que ver con la palabra clave volátil). En particular, esto ocurrió cuando he inicializado un marco de registro (por ejemplo, Crashlytics, log4j), y luego, después de algún período de actividad, parece que no está inicializado. La investigación ha demostrado que esto sucede después de que el sistema operativo llama a onSaveInstanceState(Bundle b) .

Sus variables estáticas son mantenidas por el Classloader que está contenido dentro del proceso de su aplicación. Según google:

Una característica inusual y fundamental de Android es que la vida útil de un proceso de aplicación no está directamente controlada por la propia aplicación. En su lugar, lo determina el sistema a través de una combinación de las partes de la aplicación que el sistema sabe que se están ejecutando, cuán importantes son estas cosas para el usuario y cuánta memoria general está disponible en el sistema.

http://developer.android.com/guide/topics/processes/process-lifecycle.html

Lo que eso significa para un desarrollador es que no se puede esperar que las variables estáticas permanezcan inicializadas indefinidamente. Necesitas confiar en un mecanismo diferente para la persistencia.

Una solución que he usado para mantener mi marco de registro inicializado es que todas mis Actividades extiendan una clase base en la que onCreate y onCreate inicialización y reinicio si es necesario.

Creo que la solución oficial es usar la devolución de llamada onSaveInstanceState(Bundle b) para conservar todo lo que su Actividad necesita más adelante, y luego reinicializar en onCreate(Bundle b) cuando b != null .

Google lo explica mejor:

http://developer.android.com/training/basics/activity-lifecycle/recreating.html

Tengo una pregunta específica sobre cómo funciona la carga de clases / recolección de basura en Android. Nos hemos topado con este problema unas cuantas veces y, por lo que puedo decir, Android se comporta de forma diferente a una JVM normal.

El problema es este: actualmente estamos tratando de reducir las clases singleton en la aplicación en favor de un singleton de fábrica de raíz única cuyo único propósito es administrar otras clases de administrador. Un gerente de nivel superior si lo desea. Esto nos facilita reemplazar las implementaciones en las pruebas sin optar por una solución DI completa, ya que todas las Actividades y Servicios comparten la misma referencia a esa fábrica raíz.

Así es como se ve:

public class RootFactory { private static volatile RootFactory instance; @SuppressWarnings("unused") private Context context; // I''d like to keep this for now private volatile LanguageSupport languageSupport; private volatile Preferences preferences; private volatile LoginManager loginManager; private volatile TaskManager taskManager; private volatile PositionProvider positionManager; private volatile SimpleDataStorage simpleDataStorage; public static RootFactory initialize(Context context) { instance = new RootFactory(context); return instance; } private RootFactory(Context context) { this.context = context; } public static RootFactory getInstance() { return instance; } public LanguageSupport getLanguageSupport() { return languageSupport; } public void setLanguageSupport(LanguageSupport languageSupport) { this.languageSupport = languageSupport; } // ... }

initialize se llama una vez, en Application.onCreate , es decir, antes de que se inicie cualquier actividad o servicio. Ahora, aquí está el problema: el método getInstance veces vuelve como null , ¡incluso cuando se invoca en el mismo hilo! Eso suena como que no es un problema de visibilidad; en su lugar, la retención de referencia de singleton estática en el nivel de clase parece haber sido eliminada por el recolector de basura. Tal vez estoy llegando a conclusiones aquí, pero ¿podría ser esto porque el recolector de basura de Android o el mecanismo de carga de clases puede descargar clases cuando la memoria escasea, en cuyo caso la única referencia a la instancia de singleton desaparecerá? No estoy realmente metido en el modelo de memoria de Java, pero supongo que eso no debería suceder, de lo contrario, esta forma común de implementar singletons no funcionaría en ninguna JVM, ¿no?

¿Alguna idea de por qué esto está sucediendo exactamente?

PD: se puede solucionar esto manteniendo las referencias "globales" en la instancia de una sola aplicación. Se ha demostrado que es confiable cuando se debe mantener el objeto durante toda la vida útil de una aplicación.

ACTUALIZAR

Aparentemente mi uso de volatile aquí causó cierta confusión. Mi intención era asegurar que el estado actual de la referencia estática siempre esté visible para todos los subprocesos que lo acceden. Debo hacer eso porque estoy escribiendo y leyendo esa referencia desde más de un hilo: en una aplicación ordinaria ejecutada solo en el hilo principal de la aplicación, pero en una ejecución de prueba de instrumentación, donde los objetos se reemplazan con simulacros, lo escribo desde el Instrumento de hilo y leerlo en el hilo de la interfaz de usuario. También podría haber sincronizado la llamada con getInstance , pero eso es más costoso ya que requiere reclamar un bloqueo de objeto. Consulte ¿Cuál es una forma eficiente de implementar un patrón de singleton en Java? para una discusión más detallada sobre esto.


Las referencias estáticas se borran siempre que el sistema lo considere necesario y que su aplicación no sea de nivel superior (el usuario no la está ejecutando explícitamente). Siempre que su aplicación esté minimizada y el sistema operativo desee algo más de memoria, la eliminará o la serializará en un almacenamiento fijo para su uso posterior, pero en ambos casos las variables estáticas se borrarán. Además, cada vez que su aplicación recibe un error de Force Close , todas las estadísticas también se borran. En mi experiencia, vi que siempre es mejor usar variables en el objeto Aplicación que las variables estáticas.


Nunca en mi vida he visto un miembro de datos estáticos declarado volatile . Ni siquiera estoy seguro de lo que eso significa.

Los miembros de datos estáticos existirán hasta que el proceso finalice o hasta que se deshaga de ellos (por ejemplo, null la referencia estática). El proceso puede terminarse una vez que todas las actividades y servicios estén cerrados proactivamente por el usuario (por ejemplo, el botón ATRÁS) y su código (por ejemplo, stopService() ). El proceso puede terminarse incluso con componentes en vivo si Android tiene una memoria RAM extremadamente corta, pero esto es bastante inusual. El proceso puede terminarse con un servicio en vivo si Android considera que su servicio ha estado en segundo plano demasiado tiempo, aunque puede reiniciar ese servicio dependiendo de su valor de retorno de onStartCommand() .

Las clases no se descargan, punto, menos que el proceso se termina.

Para abordar el otro de los puntos de @sergui, las actividades pueden destruirse, con el estado de la instancia almacenado (aunque en la RAM, no en el "almacenamiento fijo"), para liberar la RAM. Android tenderá a hacer esto antes de terminar los procesos activos, aunque si destruye la última actividad de un proceso y no hay servicios en ejecución, ese proceso será un candidato principal para la terminación.

La única cosa significativamente extraña acerca de su implementación es su uso de volatile .


Tanto tú (@Matthias) como Mark Murphy (@CommonsWare) tienen razón en lo que dices, pero la esencia parece estar perdida. (El uso de volatile es correcto y las clases no se descargan.)

El quid de la pregunta es desde dónde se llama a initialize .

Esto es lo que creo que está pasando:

  • Estás llamando a inicializar desde una Activity
  • Android necesita más memoria, mata todo el Process
  • Android reinicia la Application y la Activity superior
  • Llama a getInstance que devolverá null , ya que no se llamó a initialize

Corrígeme si estoy equivocado.