usage not management leak how exist example debug java memory memory-management memory-leaks garbage-collection

not - Referencias circulares en Java



memory leak example in java (10)

El recolector de basura es una pieza de software muy sofisticada; ha sido probado en un enorme banco de pruebas JCK. NO es perfecto PERO hay una posibilidad muy buena de que mientras el compilador de java (javac) compile todas tus clases y JVM lo instanciará, entonces deberías estar bien.

Por otra parte, si mantiene referencias a la raíz de este gráfico de objetos, la memoria NO se liberará PERO si sabe lo que está haciendo, debería estar bien.

Dada una agregación de instancias de clase que se refieren entre sí de forma compleja, circular: ¿es posible que el recolector de basura no pueda liberar estos objetos?

Recuerdo vagamente que esto era un problema en la JVM en el pasado, pero pensé que esto se había resuelto hace años. sin embargo, alguna investigación en jhat ha revelado que una referencia circular es la razón de una fuga de memoria que ahora enfrento.

Nota: Siempre he tenido la impresión de que la JVM era capaz de resolver referencias circulares y liberar esas "islas de basura" de la memoria. Sin embargo, estoy planteando esta pregunta solo para ver si alguien ha encontrado alguna excepción.


El recolector de basura sabe dónde están los objetos raíz: estáticos, locales en la pila, etc. y si los objetos no son accesibles desde una raíz, entonces serán recuperados. Si son alcanzables, entonces necesitan quedarse.


El recuento de referencias Los GC son notorios para este problema. En particular, Suns JVM no utiliza un GC de recuento de referencias.

Si no se puede acceder al objeto desde la raíz del montón (generalmente, como mínimo, a través de los cargadores de clases si no hay nada más0), los objetos se destruirán ya que no se copiarán durante un GC de Java típico al nuevo montón.


La especificación de Java dice que el recolector de basura puede recoger su objeto SOLAMENTE si no se puede acceder desde ningún hilo.

Alcanzable significa que hay una referencia o cadena de referencias que conduce de A a B, y puede ir a través de C, D, ... Z para todo lo que le interesa.

La JVM no recopiló cosas no ha sido un problema para mí desde el año 2000, pero su kilometraje puede variar.

Sugerencia: la serialización de Java almacena en caché los objetos para que la transferencia de malla de objeto sea eficiente. Si tiene muchos objetos grandes y transitorios, y toda su memoria está siendo trabada, reinicie su serializador para borrar su caché.


No, al menos usando la JVM oficial de Sun, el recolector de basura podrá detectar estos ciclos y liberar la memoria tan pronto como ya no haya referencias desde el exterior.


Ryan, a juzgar por su comentario en Referencias circulares en Java , cayó en la trampa de hacer referencia a objetos de una clase, que probablemente fue cargada por el cargador de clases de arranque / sistema. El cargador de clases que cargó la clase hace referencia a cada clase y, por lo tanto, solo puede recopilarse basura si el cargador de clases ya no es accesible. El inconveniente es que el cargador de clases bootstrap / system nunca se recolecta basura, por lo tanto, los objetos accesibles desde las clases cargadas por el cargador de clases del sistema tampoco se pueden recolectar basura.

El razonamiento de este comportamiento se explica en JLS. Por ejemplo, Third Edition 12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7 .


Si recuerdo correctamente, entonces, de acuerdo con las especificaciones, solo hay garantías sobre lo que la JVM no puede recopilar (todo lo que se puede alcanzar), no sobre lo que recolectará.

A menos que trabaje con JVM en tiempo real, la mayoría de los recolectores de basura modernos deberían poder manejar estructuras de referencia complejas e identificar "subgrafos" que puedan eliminarse de manera segura. La eficacia, la latencia y la probabilidad de hacerlo mejoran con el tiempo a medida que más ideas de investigación se abren paso en VM estándar (en lugar de investigación).


Solo para amplificar lo que ya se ha dicho:

La aplicación en la que he estado trabajando durante seis años cambió recientemente de Java 1.4 a Java 1.6, y descubrimos que hemos tenido que agregar referencias estáticas a cosas que ni siquiera nos habíamos dado cuenta antes de que fueran basura recolectable. No necesitamos la referencia estática antes porque el recolector de basura solía chupar, y ahora es mucho mejor.


Solo una implementación muy ingenua tendría un problema con las referencias circulares. Wikipedia tiene un buen article sobre los diferentes algoritmos de GC. Si realmente desea obtener más información, pruebe (Amazon) Garbage Collection: Algorithms for Automatic Dynamic Memory Management . Java ha tenido un buen recolector de basura desde 1.2 y uno excepcionalmente bueno en 1.5 y Java 6.

La parte más difícil para mejorar GC es reducir las pausas y los gastos generales, no cosas básicas como la referencia circular.


Una referencia circular ocurre cuando un objeto se refiere a otro, y ese otro se refiere al primer objeto. Por ejemplo:

class A { private B b; public void setB(B b) { this.b = b; } } class B { private A a; public void setA(A a) { this.a = a; } } public class Main { public static void main(String[] args) { A one = new A(); B two = new B(); // Make the objects refer to each other (creates a circular reference) one.setB(two); two.setA(one); // Throw away the references from the main method; the two objects are // still referring to each other one = null; two = null; } }

El recolector de basura de Java es lo suficientemente inteligente como para limpiar los objetos si hay referencias circulares, pero ya no hay subprocesos activos que tengan referencias a los objetos. Por lo tanto, tener una referencia circular como esta no crea una pérdida de memoria.