variable una otra misma metodo llamar internas desde como clases clase anonimas anidadas java garbage-collection anonymous-class

java - otra - como llamar una clase en netbeans



¿Las clases anónimas*siempre*mantienen una referencia a su instancia adjunta? (3)

Estoy trabajando con algún código en el que un objeto, "foo", está creando otro objeto, "barra", y le pasa un Callable . Después de esto, foo devolverá la barra, y luego quiero que foo se vuelva inalcanzable (es decir, disponible para la recolección de basura).

Mi primer pensamiento fue crear el Callable anónimamente. p.ej:

class Foo { ... public Bar createBar() { final int arg1 = ... final int arg2 = ... final int arg3 = ... return new Callable<Baz>() { @Override public Baz call() { return new Baz(arg1, arg2, arg3); } }; } }

Se me ocurrió que esto podría no funcionar realmente como se desea, sin embargo, como una clase interna normalmente mantiene una referencia a su objeto de encierro. No quiero una referencia a la clase adjunta aquí, porque quiero que se recoja el objeto que lo contiene mientras el Callable se puede Callable .

Por otro lado, detectar que nunca se hace referencia a la instancia adjunta debería ser bastante trivial, por lo que quizás el compilador de Java sea lo suficientemente inteligente como para no incluir una referencia en ese caso.

Entonces ... ¿se mantendrá una instancia de una clase interna anónima en una referencia a su instancia adjunta, incluso si en realidad nunca usa la referencia de instancia adjunta?


La alternativa estática (en este caso) no es mucho más grande (1 línea):

public class Outer { static class InnerRunnable implements Runnable { public void run() { System.out.println("hello"); } } public Runnable getRunnable() { return new InnerRunnable(); } }

Por cierto: si utiliza un Lambda en Java8, no se generará ninguna clase anidada. Sin embargo, no estoy seguro de si los objetos de CallSite que se pasan en ese caso contienen una referencia a la instancia externa (si no es necesaria).


Puede convertir fácilmente una clase anónima anidada en una clase anónima "estática" introduciendo un método estático en su clase.

import java.util.ArrayList; public class TestGC { public char[] mem = new char[5000000]; public String str = "toto"; public interface Node { public void print(); } public Node createNestedNode() { final String str = this.str; return new Node() { public void print() { System.out.println(str); } }; } public static Node createStaticNode(TestGC test) { final String str = test.str; return new Node() { public void print() { System.out.println(str); } }; } public Node createStaticNode() { return createStaticNode(this); } public static void main(String... args) throws InterruptedException { ArrayList<Node> nodes = new ArrayList<Node>(); for (int i=0; i<10; i++) { // Try once with createNestedNode(), then createStaticNode() nodes.add(new TestGC().createStaticNode()); System.gc(); //Thread.sleep(200); System.out.printf("Total mem: %d Free mem: %d/n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory()); } for (Node node : nodes) node.print(); nodes = null; System.gc(); //Thread.sleep(200); System.out.printf("Total mem: %d Free mem: %d/n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory()); } }


Sí, las instancias de clases internas anónimas conservan una referencia a sus instancias adjuntas, incluso si estas referencias nunca se usan realmente. Este codigo

public class Outer { public Runnable getRunnable() { return new Runnable() { public void run() { System.out.println("hello"); } }; } }

Cuando se compila con javac genera dos archivos de clase, Outer.class y Outer$1.class . Desmontando este último, la clase interna anónima, con javap -c produce:

Compiled from "Outer.java" class Outer$1 extends java.lang.Object implements java.lang.Runnable{ final Outer this$0; Outer$1(Outer); Code: 0: aload_0 1: aload_1 2: putfield #1; //Field this$0:LOuter; 5: aload_0 6: invokespecial #2; //Method java/lang/Object."<init>":()V 9: return public void run(); Code: 0: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #4; //String hello 5: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }

La línea putfield muestra que el constructor está almacenando una referencia a la instancia putfield en el campo a this$0 (de tipo Outer ) aunque este campo nunca se vuelva a utilizar.

Esto es desafortunado si está intentando crear pequeños objetos potencialmente longevos con clases internas anónimas, ya que se mantendrán en la instancia adjunta (potencialmente grande). Una solución es usar una instancia de una clase estática (o una clase de nivel superior) en su lugar. Esto es, lamentablemente, más detallado.