2010-04-12 20 views
30

He leído en alguna parte que no es una buena idea usar las instancias de la clase siguiente, ya que pueden causar pérdidas de memoria. ¿Puede alguien decirme si es una declaración válida? ¿O hay algún problema al usarlo de esta manera?¿Está utilizando la instancia de Clase como clave de mapa una mejor práctica?

Map<Class<?>,String> classToInstance = new HashMap(); 

classToInstance.put(String.class,"Test obj"); 
+0

No estoy seguro de que pueda causar pérdidas de memoria ... solo significa que el mapa hace referencia a la versión estática de String (utilizada por sus métodos estáticos). – Powerlord

+1

Los problemas de pérdida de memoria son (por ejemplo) cuando se cuelga una referencia a un objeto o clase de un mapa de mayor duración. Por ejemplo, si en una aplicación web registra un controlador JDBC de su guerra (DriverManager.ergisterDriver), la clase DriverManager, cargada por el cargador de clases bootstrap (no el cargador de clases webapp) mantiene una referencia a su clase de controlador. Por lo tanto, cuando descarga la aplicación web, no puede descargar el cargador de clases de aplicaciones web (debido a la clase de controlador enganchado) y ninguna de las otras clases de aplicaciones web está descargada. Y eso no es bueno – helios

+0

Me gustaría saber dónde oíste este concepto de fuga? ¿Puedes recordar algún detalle sobre dónde oíste esto? ¿Fue posiblemente en el contexto de un entorno con múltiples clasificadores (por ejemplo, contenedor de aplicaciones web)? –

Respuesta

18

Sí, ¡debe ser precavido! Por ejemplo, si su código se está ejecutando en un contenedor web y tiene el hábito de realizar un despliegue activo de webapps, una referencia retenida a un único objeto de clase puede causar una pérdida significativa de memoria permgen.

This article explica el problema en detalle. Pero en pocas palabras, el problema es que cada clase contiene una referencia a su cargador de clases, y cada cargador de clases contiene referencias a cada clase que ha cargado. Entonces, si una clase es alcanzable, todas lo son.


De Java 8 - PermGen fue eliminado. ¿Crees que está bien usar la instancia de Clase como clave HashMap en cualquier situación?

Tenga en cuenta que todavía tendrá una pérdida de memoria. Cualquier clase cargada dinámicamente utilizada en su HashMap (clave o valor) y (al menos) otras clases cargadas dinámicamente se mantendrán accesibles. Esto significa que el GC no podrá descargarlos ni eliminarlos. Lo que antes era una fuga de permgen ahora es una fuga de montón normal.

+0

de Java 8 - Permgen fue eliminado. ¿Crees que está bien usar la instancia de Clase como clave HashMap en cualquier situación? ¡Gracias! – Loc

5

No, eso no es un problema. Siempre que esté creando una instancia de la clase de todos modos, no estará utilizando más memoria manteniendo una referencia a la clase en sí.

+0

Interesante pregunta. Puedo ver cómo no se requiere memoria adicional aquí, pero ¿la referencia a un objeto de clase evitará que la claseToInstanceMap sea basura? –

+1

No es un problema ... excepto que la instancia del mapa cuelga de una clase u objeto de mayor duración que la clase que se utiliza como clave. En ese caso, la clase "clave" no se recoge basura porque el mapa todavía está allí ... Si el mapa tiene una vida corta, o al menos no está suspendido de alguna clase base JVM, está bien. – helios

+2

@Chris Knight: No, la accesibilidad solo avanza. 'classToInstanceMap' que tiene una referencia a' String.class' evitará que 'String.class' se recopile, pero no al revés. 'String.class' puede referirse a un bloque de información de tipo que no es elegible para la recopilación de datos, pero no sé lo suficiente sobre la JVM para decirlo con seguridad. –

0

Como mencionó Stephen C, la pérdida de memoria se debe a los cargadores de clases. Pero el problema es más agudo que a primera vista. Considere esto:

mapkey --> class --> classloader --> all other classes defined by this classloader. 

Además,

class --> any static members, including static Maps e.g. caches. 

Unos dichas cachés estáticas pueden empezar a añadir hasta cantidades graves de la memoria pierde cada vez que una aplicación web o alguna otra forma dinámica (classloaded) aplicación de carga se realiza un ciclo.

Existen varias formas de solucionar este problema. Si no le importan las diferentes "versiones" de la misma clase de diferentes cargadores de clases, simplemente la clave se basa en Class.getName(), que es java.lang.String.

Otra opción es usar java.util.WeakHashMap. Esta forma de mapa solo mantiene referencias débiles a las claves. Las referencias débiles no retienen GC, por lo que la clave no causará acumulación de memoria. Sin embargo, los valores son no a los que se hace referencia débilmente. Por lo tanto, si los valores son, por ejemplo, instancias de las clases utilizadas como claves, el WeakHashMap no funciona.

0

Depende de dónde se defina la referencia classToInstance=new HashMap();.

Una clase señala a su cargador de clases, y como consecuencia, el cargador de clases no se puede recolectar basura mientras existe una referencia a la clase. Pero si las referencias forman un círculo (o un grupo inaccesible), esto sigue funcionando: el GC sabe cómo manejar las referencias circulares.

parent class loader --> class loader <--> class // no GC is possible 
parent class loader  class loader <--> class // circular references are GC'ed 

lo tanto, una referencia a una clase puede evitar que el cargador de clases de ser GC'ed sólo si las referencias provienen de un objeto/clase en una matriz cargador de clase.

parent class loader  class loader <--> class // no GC is possible 
        \-------------------/ 

Eso es lo que la frase "cualquier referencia desde fuera de la aplicación a un objeto en la aplicación de la que la clase se carga por cargador de clases de la aplicación provocará una fuga cargador de clases" significa en el article mencionado en Stephen C . responder.

Pero no debería ser el caso si el mapa es parte de su aplicación.

Cuestiones relacionadas