verificar descargar java

descargar - java offline



¿Los oyentes Java deben ser eliminados?(En general) (12)

Imagine esta clase de ejemplo de Java:

class A { void addListener(Listener obj); void removeListener(Listener obj); } class B { private A a; B() { a = new A(); a.addListener(new Listener() { void listen() {} } }

¿Debo agregar un método de finalización a B para llamar a.removeListener? Supongamos que la instancia A también se compartirá con otros objetos y sobrevivirá a la instancia B.

Me preocupa que pueda estar creando un problema con el recolector de basura aquí. cual es la mejor practica?


¿Cómo puede A sobrevivir B ?:

Ejemplo de uso de B y A:

public static main(args) { B myB = new B(); myB = null; }

Comportamiento que esperaría:

GC eliminará myB y en la instancia de myB solo hará referencia a la instancia A, por lo que también se eliminará. ¿Con todos sus oyentes asignados?

¿Quizás quisiste decir:

class B { private A a; B(A a) { this.a = a; a.addListener(new Listener() { void listen() {} } }

Con el uso:

public static main(args) { A myA = new A(); B myB = new B(myA); myB = null; }

Porque entonces realmente me preguntaría qué le sucede a esa clase anónima ...


A contiene una referencia a B a través de la instancia anónima utilizada implícitamente por el tipo anónimo creado. Esto significa que B no se liberará hasta que se llame a removeListener, y por lo tanto no se llamará a B''s finalize.

Cuando A se destruye, su referencia anónima a B también destruirá abriendo el camino para que B sea liberado.

Pero dado que B contiene una referencia a A, esto nunca sucede. Esto parece un problema de diseño: si A tiene llamadas para escuchar, ¿por qué necesita que B también tenga una referencia a A? ¿Por qué no pasar la A que hizo la llamada al oyente, si es necesario?


A mantendrá vivo a B a través de la instancia anónima.

Pero no anularé la finalización para abordar eso, sino que usaré una clase interna estática que no mantenga viva la B.


A, de hecho, evitará que B se recolecte basura en usted, ya que utiliza referencias estándar para almacenar sus oyentes. Alternativamente, cuando mantiene listas de oyentes en lugar de definir nuevas ArrayList <ListenerType> (); podría hacer algo como ArrayList <WeakReference <ListenerType >> ();

Al envolver su objeto en una Referencia débil, puede evitar que prolongue la vida del objeto.

Esto solo funciona por supuesto si estás escribiendo la clase que contiene a los oyentes


Basándose en lo que @Alexander dijo acerca de eliminarse como oyente:

A menos que exista alguna razón convincente para no hacerlo, una cosa que he aprendido de mis compañeros de trabajo es que en lugar de crear un Oyente interno anónimo, y que necesitan almacenarlo en una variable, hacen que B implemente Oyente, y luego B puede eliminarse cuando sea necesario con a.removeListener(this)


Cuando el B es basura recogida, debe permitir que el A también sea basura, y por lo tanto cualquier referencia en A también. No necesita eliminar explícitamente las referencias en A.

No conozco ningún dato sobre si lo que sugieres haría que el recolector de basura funcionara más eficientemente, sin embargo, y cuando valga la pena molestarlo, pero me interesaría verlo.


Debe venir de C ++ o de algún otro idioma donde las personas implementen destructores. En Java, no haces eso. No anula la finalización a menos que realmente sepa lo que está haciendo. En 10 años, nunca tuve que hacer eso, y todavía no puedo pensar en una buena razón que me obligue a hacerlo.

Volviendo a su pregunta, su oyente es un objeto independiente con su propio ciclo de vida y se recopilará después de que se recopilen todos los demás objetos que lo referencian o cuando ningún otro objeto lo señale. Esto funciona muy bien Entonces no, no tienes que anular la finalización.


En su situación, el único "problema" de recolección de basura es que las instancias de B no se recolectarán como basura mientras haya referencias duras a la instancia compartida de A Así es como se supone que la recolección de basura funciona en Java / .NET. Ahora, si no te gusta el hecho de que las instancias de B no se recogen antes, ¿tienes que preguntarte en qué momento quieres que dejen de escuchar eventos de A ? Una vez que tenga la respuesta, sabrá cómo arreglar el diseño.


Hay un ciclo en el gráfico de referencia. Las referencias B y B hacen referencia a A. El recolector de basura detectará ciclos y verá cuándo no hay referencias externas a A y B, y luego recopilará ambos.

Intentar utilizar el finalizador aquí es incorrecto. Si B se destruye, la referencia a A también se está eliminando.

La afirmación: "Supongamos que la instancia A también se compartirá con otros objetos y sobrevivirá a la instancia B". Está Mal. La única forma en que sucederá es si el oyente se elimina explícitamente de un lugar que no sea un finalizador. Si las referencias a A se pasan, eso implicará una referencia a B, y B no será basura porque hay referencias externas al ciclo AB.

Actualización adicional:

Si desea interrumpir el ciclo y no necesita B para eliminar explícitamente el oyente, puede usar una WeakReference. Algo como esto:

class A { void addListener(Listener obj); void removeListener(Listener obj); } class B { private static class InnerListener implements Listener { private WeakReference m_owner; private WeakReference m_source; InnerListener(B owner, A source) { m_owner = new WeakReference(owner); m_source = new WeakReference(source); } void listen() { // Handling reentrancy on this function left as an excercise. B b = (B)m_owner.get(); if (b == null) { if (m_source != null) { A a = (A) m_source.get(); if (a != null) { a.removeListener(this); m_source = null; } } return; } ... } } private A a; B() { a = new A(); a.addListener(new InnerListener(this, a)); } }

Podría ser generalizado si es necesario en múltiples clases.


Mi comprensión del GC es que, hasta que se llame al método removeListener, la clase A mantendrá una referencia al oyente y, por lo tanto, no será candidata para la limpieza del GC (y, por lo tanto, no se invocará la finalización).


Si ha agregado B como un oyente a A, y A debe superar a B, nunca se llamará a la llamada a finalizar en B porque hay una instancia de B dentro de A, por lo que nunca se obtendrá basura. Podría evitar esto almacenando una referencia a B en A como WeakReference (que no se considera una referencia durante la recopilación de garaje), pero sería mejor anular el registro explícito de B de A cuando ya no lo necesite.

En general, se recomienda en Java no utilizar el método de finalización en Java porque nunca se puede estar seguro de cuándo se llamará y no se puede usar para anular el registro de otra clase.


Acabo de encontrar una gran pérdida de memoria, así que voy a llamar al código que creó la fuga para que esté mal y mi solución no se filtre como correcta .

Aquí está el código anterior: (Este es un patrón común que he visto en todas partes)

class Singleton { static Singleton getInstance() {...} void addListener(Listener listener) {...} void removeListener(Listener listener) {...} } class Leaky { Leaky() { // If the singleton changes the widget we need to know so register a listener Singleton singleton = Singleton.getInstance(); singleton.addListener(new Listener() { void handleEvent() { doSomething(); } }); } void doSomething() {...} } // Elsewhere while (1) { Leaky leaky = new Leaky(); // ... do stuff // leaky falls out of scope }

Claramente, esto es malo. Se están creando muchos Leaky''s y nunca se recolecta basura porque los oyentes los mantienen vivos.

Aquí estaba mi alternativa que solucionó mi pérdida de memoria. Esto funciona porque solo me importa el oyente del evento mientras existe el objeto. El oyente no debe mantener vivo el objeto.

class Singleton { static Singleton getInstance() {...} void addListener(Listener listener) {...} void removeListener(Listener listener) {...} } class NotLeaky { private NotLeakyListener listener; NotLeaky() { // If the singleton changes the widget we need to know so register a listener Singleton singleton = Singleton.getInstance(); listener = new NotLeakyListener(this, singleton); singleton.addListener(listener); } void doSomething() {...} protected void finalize() { try { if (listener != null) listener.dispose(); } finally { super.finalize(); } } private static class NotLeakyListener implements Listener { private WeakReference<NotLeaky> ownerRef; private Singleton eventer; NotLeakyListener(NotLeaky owner, Singleton e) { ownerRef = new WeakReference<NotLeaky>(owner); eventer = e; } void dispose() { if (eventer != null) { eventer.removeListener(this); eventer = null; } } void handleEvent() { NotLeaky owner = ownerRef.get(); if (owner == null) { dispose(); } else { owner.doSomething(); } } } } // Elsewhere while (1) { NotLeaky notleaky = new NotLeaky(); // ... do stuff // notleaky falls out of scope }