2010-08-22 11 views
13

Me refiero a este ejemplo de código, el cual se está informando en http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6254531Por qué el objeto creado por el cargador de clases no tienen la oportunidad de recoger la basura en sí

import java.net.URL; 

class Loader { 
    public static void main(String[] args) throws Exception { 
     for (;;) { 
      System.gc(); 
      System.out.print("."); 
      System.out.flush(); 
      new java.net.URLClassLoader(
       new URL[] { new java.io.File(".").toURL() }, 
       ClassLoader.getSystemClassLoader().getParent() 
      ).loadClass("Weakling").newInstance(); 
     } 
    } 
} 
public class Weakling { 
    private static ThreadLocal<Object> local; 
    private static Weakling staticRef; 
    private Object var = new byte[1000*1000]; 
    public Weakling() { 
     local = new ThreadLocal<Object>(); 
     local.set(this); 
     staticRef = this; 
    } 

    @Override 
    protected void finalize() { 
     System.out.print("F"); 
     System.out.flush(); 
    } 
} 

La finalización nunca será llamado. Sin embargo, si cambio el

  new java.net.URLClassLoader(
       new URL[] { new java.io.File(".").toURL() }, 
       ClassLoader.getSystemClassLoader().getParent() 
      ).loadClass("Weakling").newInstance(); 

a

new Weakling(); 

Funciona muy bien y no hay fuga detectada.

¿Alguien puede explicar por qué el objeto creado por ClassLoader no tiene la posibilidad de acumularse basura?

+0

Quizás pueda replantear su pregunta o aclarar qué parte de mi respuesta no comprende. –

+0

He actualizado el comentario. –

Respuesta

21

El mecanismo ThreadLocal almacena efectivamente en el hilo actual un WeakHashMap de instancias de ThreadLocal en valores. En consecuencia, si la instancia de ThreadLocal nunca se vuelve débilmente referenciable, entonces la entrada se filtra de manera efectiva.

Hay dos casos a considerar. Por la simplicidad de la discusión, supongamos que ThreadLocal realmente almacena un WeakHashMap en Thread.currentThread(); en realidad, usa un mecanismo más sofisticado que tiene un efecto equivalente.

Primero en cuenta la situación "nueva Weakling":

  • En la primera iteración del bucle:
    1. la clase Weakling se carga desde el cargador de clases del sistema
    2. el constructor Weakling se llama
    3. la variable estática Weakling.local se establece desde nulo en una nueva instancia ThreadLocal # 1
    4. ThreadLocal WeakHashMap se actualiza para almacenar la nueva instancia Weakling # 1
  • En todas las iteraciones posteriores del bucle:
    1. la clase Weakling ya se carga desde el cargador de clases sistema
    2. el constructor Weakling se llama
    3. la variable estática Weakling.local se establece desde la instancia anterior ThreadLocal # 1 a una nueva instancia ThreadLocal # 2. La vieja instancia de ThreadLocal # 1 ahora es referenciada (débilmente) por WeakHashMap.
    4. ThreadLocal WeakHashMap se ha actualizado para almacenar la nueva instancia de Weakling. Durante esta operación, WeakHashMap nota que la instancia anterior ThreadLocal # 1 es solo de referencia débil, por lo que elimina la entrada [instancia ThreadLocal # 1, Weakling # 1] del Mapa antes de agregar la [instancia ThreadLocal # 2, Weakling # 2 ] entrada.

En segundo lugar consideran que la "nueva URLClassLoader (...). LoadClass (...).newInstance()" escenario:

  • En la primera iteración del bucle: clase
    1. la Weakling # 1 se carga desde URLClassLoader # 1
    2. el constructor Weakling se llama
    3. la Weakling. local # 1 variable estática se establece desde nulo a una nueva instancia ThreadLocal # 1
    4. la ThreadLocal WeakHashMap se actualiza para almacenar la nueva instancia Weakling # 1
  • En todas las iteraciones posteriores del bucle
    1. la #n clase Weakling se carga desde URLClassLoader #n
    2. el constructor Weakling se llama
    3. la Weakling.local #n variable estática se establece de NULL a una nueva instancia ThreadLocal #n
    4. ThreadLocal WeakHashMap se ha actualizado para almacenar la nueva instancia Weakling.

Tenga en cuenta que durante este paso final, instancia ThreadLocal # 1 es no débilmente referenciable. Esto es debido a la siguiente cadena de referencia:

  • valor WeakHashMap referencia fuertemente Weakling ejemplo # 1
  • Weakling ejemplo # 1 Referencias fuertemente Weakling clase # 1 a través de Object.getClass()
  • Weakling clase # 1 fuertemente referencias ThreadLocal ejemplo # 1 a través de la clase variable estática

mientras el bucle continúa funcionando, se añaden más entradas a la ThreadLocal WeakHashMap, y la fuerte cadena de referencia de valor-a-clave (ejemplo Weakling a ThreadLocal) en el WeakH ashMap evita la recolección de basura de entradas que de otro modo estarían obsoletas.

He modificado el programa Loader para que se repita 3 veces y luego espere la entrada del usuario. Luego, generé un volcado de pila usando java -Xrunhprof: heap = dump y ctrl-pause/break. Lo que sigue es mi análisis de la final de volcado de pila:

En primer lugar, hay tres objetos Weakling:

OBJ 500002a1 (sz=16, trace=300345, [email protected]) 
OBJ 500003a4 (sz=16, trace=300348, [email protected]) 
OBJ 500003e0 (sz=16, trace=300342, [email protected]) 

Tenga en cuenta que los tres casos Weakling (500002a1, 500003a4 y 500003e0) se crean a partir de tres clases distintas instancias (50000296, 5000039d y 500003d9, respectivamente).Mirando el primer objeto, podemos ver que se lleva a cabo como un valor en un objeto entrada en el mapa ThreadLocal:

OBJ 500002a5 (sz=32, trace=300012, [email protected]) 
     referent  500002a4 
     queue   500009f6 
     value   500002a1 

El referente aquí es el valor que se celebra débilmente:

OBJ 500002a4 (sz=16, trace=300347, [email protected]) 

búsqueda, podemos ver que este objeto se lleva a cabo como un valor en la variable estática "local" de la clase mencionada Weakling:

CLS 50000296 (name=Weakling, trace=300280) 
     super   50000099 
     loader   5000017e 
     domain   50000289 
     static local 500002a4 
     static staticRef  500002a1 

en conclusión, tenemos el siguiente fuerte bucle de cadena de referencia para esta instancia de debilitamiento, que evita que se recolecte basura.

  • valor WeakHashMap (500002a5) hace referencia a fuertemente ejemplo Weakling (500002a1)
  • ejemplo Weakling (500002a1) fuertemente referencias de clase Weakling (50,000,296) a través de Object.getClass()
  • clase Weakling (50000296) Referencias fuertemente ThreadLocal instancia (500002a4) a través de la variable de clase estática

Un análisis similar en los otros objetos Weakling mostraría un resultado similar. Permitir que el programa se ejecute para iteraciones adicionales muestra que los objetos continúan acumulándose de esta manera.

+0

¿Por qué la referencia fuerte a la instancia de Weakling todavía está allí si estoy usando URLClassLoader? –

+0

¿Por qué la clase contiene una referencia fuerte a la instancia de ThreadLocal? – EJP

+0

@Yan: la fuerte referencia a la instancia de Weakling está allí independientemente de si usa URLClassLoader o no. La diferencia es que cuando usa "nuevo Weakling", solo hay una instancia de la clase Weakling (la cargada por el cargador de clases del sistema), por lo que cada nueva instancia creada elimina la referencia fuerte de ThreadLocal, lo que permite que sea basura recogido. –

Cuestiones relacionadas