Fuga de memoria de Android en JarURLConnectionImpl de Apache Harmony?

Estoy trabajando en una aplicación para Android y estamos investigando el uso de la memoria.

Mirando un volcado de montón de hprof, estamos viendo casi 2M (22% de nuestro montón) que se utiliza en una caché estática en JarURLConnectionImpl:

Introduzca aquí la descripción de la imagen

Observando el código fuente de JarURLConnectionImpl , parece que las entradas se agregan a la variable jarCache estática, pero nunca se eliminan.

Si es cierto que nunca se eliminan, eso me parece una posible pérdida de memoria.

¿Es esto una fuga? ¿Hay una solución o solución?

He aquí una solución fea:

private static HashMap<URL,JarFile> jarCache; static { try { Class<?> jarURLConnectionImplClass = Class.forName("org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl"); final Field jarCacheField = jarURLConnectionImplClass.getDeclaredField("jarCache"); jarCacheField.setAccessible(true); //noinspection unchecked jarCache = (HashMap<URL, JarFile>) jarCacheField.get(null); } catch(Exception e) { // ignored } } 

A continuación, ejecute periódicamente lo siguiente:

  // HACK http://stackoverflow.com/questions/14610350/android-memory-leak-in-apache-harmonys-jarurlconnectionimpl if( jarCache!=null ) { try { for ( final Iterator<Map.Entry<URL, JarFile>> iterator = jarCache.entrySet().iterator(); iterator.hasNext(); ) { final Map.Entry<URL, JarFile> e = iterator.next(); final URL url = e.getKey(); if (Strings.toString(url).endsWith(".apk")) { Log.i(TAG,"Removing static hashmap entry for " + url); try { final JarFile jarFile = e.getValue(); jarFile.close(); iterator.remove(); } catch( Exception f ) { Log.e(TAG,"Error removing hashmap entry for "+ url,f); } } } } catch( Exception e ) { // ignored } } 

Lo ejecuto en la creación de actividad para que se ejecute cada vez que se crea una de mis actividades. La fea entrada hashmap no parece ser recreada todo lo que a menudo, pero parece reaparecer de vez en cuando, por lo que no es suficiente para ejecutar este código una vez.

Esta es definitivamente una pérdida de memoria desagradable. He abierto un problema para él ya que nadie más parece haberlo informado.

Gracias por la "horrible solución" emmby, que fue útil. Un enfoque más seguro, aunque potencialmente con un impacto en el rendimiento, es desactivar el almacenamiento en caché URLConnection por completo. Dado que el indicador URLConnection.defaultUseCaches es estático y, como se puede adivinar, es el valor por defecto para el indicador useCaches de cada instancia, sólo puede configurarlo como false y no más instancias almacenarán sus conexiones en caché. Esto afectará a todas las implementaciones de URLConnection, por lo que puede tener efectos de mayor alcance de lo deseado, pero creo que es un trade-off razonable.

Simplemente puede crear una clase simple como esta e instanciarla muy pronto en la aplicación onCreate ():

 public class URLConnectionNoCache extends URLConnection { protected URLConnectionNoCache(URL url) { super(url); setDefaultUseCaches(false); } public void connect() throws IOException { } } 

Lo interesante es que, puesto que esto ocurre después de cargar y ejecutar tu aplicación, las libs del sistema ya deberían estar en caché, y esto sólo evitará el almacenamiento en caché, por lo que probablemente da el mejor equilibrio posible: no poner en caché tu apk mientras Ventajas del funcionamiento de almacenar en caché las jarras del sistema.

Antes de hacer esto, modifiqué la solución de emmby un poco para convertirla en una clase autónoma que crea un subproceso de fondo para limpiar periódicamente el caché. Y lo restringí para borrar el apk de la aplicación, aunque eso puede ser relajado si lo desea. La cosa a preocuparse aquí es que usted está modificando los objetos mientras pueden estar en uso, que generalmente no es una buena cosa. Si desea seguir esta ruta, sólo tiene que llamar al método start () con un contexto, por ejemplo, en la aplicación onCreate () de su aplicación.

 package com.example; import java.lang.reflect.Field; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.jar.JarFile; import java.util.regex.Pattern; import android.content.Context; // hack to remove memory leak in JarURLConnectionImpl // from http://stackoverflow.com/questions/14610350/android-memory-leak-in-apache-harmonys-jarurlconnectionimpl public class JarURLMonitor { private static JarURLMonitor instance; private Pattern pat; private Field jarCacheField; public volatile boolean stop; private static final long CHECK_INTERVAL = 60 * 1000; public static synchronized void start(Context context) { if (instance == null) { instance = new JarURLMonitor(context); } } public static synchronized void stop() { if (instance != null) { instance.stop = true; } } private JarURLMonitor(Context context) { // get jar cache field try { final Class<?> cls = Class.forName("libcore.net.url.JarURLConnectionImpl"); jarCacheField = cls.getDeclaredField("jarCache"); jarCacheField.setAccessible(true); } catch (Exception e) { // log } if (jarCacheField != null) { // create pattern that matches our package: eg /data/app/<pkgname>-1.apk pat = Pattern.compile("^.*/" + context.getPackageName() + "-.*\\.apk$"); // start background thread to check it new Thread("JarURLMonitor") { @Override public void run() { try { while (!stop) { checkJarCache(); Thread.sleep(CHECK_INTERVAL); } } catch (Exception e) { // log } } }.start(); } } private void checkJarCache() throws Exception { @SuppressWarnings("unchecked") final HashMap<URL, JarFile> jarCache = (HashMap<URL, JarFile>)jarCacheField.get(null); final Iterator<Map.Entry<URL, JarFile>> iterator = jarCache.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry<URL, JarFile> entry = iterator.next(); final JarFile jarFile = entry.getValue(); final String file = jarFile.getName(); if (pat.matcher(file).matches()) { try { jarFile.close(); iterator.remove(); } catch (Exception e) { // log } } } } } 
  • Android: EditText causando una pérdida de memoria
  • La aplicación de Android se bloquea con WIN DEATH después de una actividad intensiva en memoria
  • Admob Memory Leak - evitando usar la actividad vacía
  • Mi aplicación Android consume demasiada memoria
  • AdView causa pérdida de memoria
  • Seguimiento de la memoria / fugas de la ventana en Android?
  • Fuga de actividad al usar oyentes de voleibol
  • Esta clase Handler debe ser estática o pueden producirse fugas: handler final
  • Herramienta de análisis de memoria en android?
  • Posible pérdida de memoria a través de FinalizerReference
  • Java.lang.OutOfMemoryError: tamaño de mapa de bits supera el presupuesto de VM
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.