universidades tesis solidos sobre residuos reciclaje reciclables proyecto por municipales manejo ley japon contaminacion java garbage-collection

java - sobre - tesis de residuos solidos municipales



¿Por qué un objeto declarado en el método está sujeto a la recolección de basura antes de que regrese el método? (7)

¿Obj estará sujeto a la recolección de basura antes de que vuelva foo ()?

No puede estar seguro de que obj será recolectado antes de que devuelva foo pero ciertamente es elegible para la recolección antes de que devuelva foo .

¿Alguien puede explicar por qué sucede esto?

Los GC recogen objetos inalcanzables. Es probable que tu objeto sea inalcanzable antes de que devuelva foo .

El alcance es irrelevante. La idea de que obj permanece en la pila hasta que devuelve foo es un modelo mental excesivamente simplista. Los sistemas reales no funcionan así.

Considere un objeto declarado en un método:

public void foo() { final Object obj = new Object(); // A long run job that consumes tons of memory and // triggers garbage collection }

¿Obj estará sujeto a la recolección de basura antes de que vuelva foo ()?

ACTUALIZACIÓN : Anteriormente, pensé que obj no está sujeto a la recolección de basura hasta que devuelva foo ().

Sin embargo, hoy me encuentro equivocado.

Pasé varias horas solucionando un error y finalmente descubrí que el problema es causado por la basura recolectada.

¿Alguien puede explicar por qué sucede esto? Y si quiero que obj se fije en cómo lograrlo?

Aquí está el código que tiene problema.

public class Program { public static void main(String[] args) throws Exception { String connectionString = "jdbc:mysql://<whatever>"; // I find wrap is gc-ed somewhere SqlConnection wrap = new SqlConnection(connectionString); Connection con = wrap.currentConnection(); Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); ResultSet rs = stmt.executeQuery("select instance_id, doc_id from crawler_archive.documents"); while (rs.next()) { int instanceID = rs.getInt(1); int docID = rs.getInt(2); if (docID % 1000 == 0) { System.out.println(docID); } } rs.close(); //wrap.close(); } }

Después de ejecutar el programa Java, imprimirá el siguiente mensaje antes de que se bloquee:

161000 161000 ******************************** Finalizer CALLED!! ******************************** ******************************** Close CALLED!! ******************************** 162000 Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:

Y aquí está el código de la clase SqlConnection:

class SqlConnection { private final String connectionString; private Connection connection; public SqlConnection(String connectionString) { this.connectionString = connectionString; } public synchronized Connection currentConnection() throws SQLException { if (this.connection == null || this.connection.isClosed()) { this.closeConnection(); this.connection = DriverManager.getConnection(connectionString); } return this.connection; } protected void finalize() throws Throwable { try { System.out.println("********************************"); System.out.println("Finalizer CALLED!!"); System.out.println("********************************"); this.close(); } finally { super.finalize(); } } public void close() { System.out.println("********************************"); System.out.println("Close CALLED!!"); System.out.println("********************************"); this.closeConnection(); } protected void closeConnection() { if (this.connection != null) { try { connection.close(); } catch (Throwable e) { } finally { this.connection = null; } } } }


A medida que se escribe su código, el objeto al que apunta "wrap" no debería ser elegible para la recolección de elementos no utilizados hasta que "wrap" salga de la pila al final del método.

El hecho de que se esté recopilando me sugiere que el código compilado no coincide con la fuente original y que el compilador ha realizado algunas optimizaciones, como cambiar esto:

SqlConnection wrap = new SqlConnection(connectionString); Connection con = wrap.currentConnection();

a esto:

Connection con = new SqlConnection(connectionString).currentConnection();

(O incluso alinear todo) porque "wrap" no se usa más allá de este punto. El objeto anónimo creado sería elegible para GC inmediatamente.

La única forma de estar seguro es descompilar el código y ver qué se le ha hecho.


Aquí, obj es una variable local en el método y se saca de la pila tan pronto como el método retorna o sale. Esto no deja forma de alcanzar el objeto Object en el montón y por lo tanto será basura recolectada. Y el objeto Object en el montón será GC''d solo después de que su obj referencia salga de la pila, es decir, solo después de que el método finalice o regrese.


EDITAR: Para responder a su actualización,

UPDATE: Let me make the question more clear. Will obj be subject to garbage collection before foo() returns?

obj es solo una referencia al objeto real en el montón . Aquí obj se declara dentro del método foo (). ¿Entonces su pregunta Will obj be subject to garbage collection before foo() returns? no se aplica porque obj va dentro del marco de la pila cuando el método foo() está ejecutando y desaparece cuando el método finaliza.


Como estoy seguro de que sabes, en Java Garbage Collection y Finialization no son deterministas. Todo lo que puede determinar en este caso es cuándo la wrap es elegible para la recolección de basura. Supongo que está preguntando si el wrap solo es elegible para GC cuando el método retorna (y el wrap queda fuera del alcance). Creo que algunas JVM (por ejemplo, HotSpot with -server ) no esperarán a que la referencia de objeto se extraiga de la pila, lo harán elegible para GC tan pronto como nada más lo haga referencia. Parece que esto es lo que estás viendo.

Para resumir, confía en que la finalización sea lo suficientemente lenta como para no finalizar la instancia de SqlConnection antes de que el método finalice. El finalizador está cerrando un recurso del que SqlConnection ya no es responsable. En su lugar, debe dejar que el objeto Connection sea ​​responsable de su propia finalización.


De acuerdo con la especificación actual, no hay ni siquiera un suceso, antes de ordenar desde la finalización hasta el uso normal. Por lo tanto, para imponer el orden, realmente necesita usar un candado, un elemento volátil o, si está desesperado, esconder una referencia accesible desde un dispositivo estático. Ciertamente, no hay nada especial sobre el alcance.

Debería ser raro que realmente necesite escribir un finalizador.


En realidad, hay dos cosas diferentes que suceden aquí. obj es una variable de pila que se establece en una referencia al Object , y el Object se asigna en el montón. La pila simplemente se borrará (mediante aritmética del puntero de la pila).

Pero sí, el Object sí será borrado por la recolección de basura. Todos los objetos asignados en el montón están sujetos a GC.

EDITAR: para responder a su pregunta más específica, la especificación de Java no garantiza la recopilación en un momento determinado (consulte la especificación de JVM ) (por supuesto, se recopilará después de su último uso). Por lo tanto, solo es posible responder para implementaciones específicas.

EDITAR: Aclaración, por comentarios


Estoy realmente asombrado por esto, pero tienes razón. Es fácilmente reproducible, no necesita perder el tiempo con las conexiones de bases de datos y cosas por el estilo:

public class GcTest { public static void main(String[] args) { System.out.println("Starting"); Object dummy = new GcTest(); // gets GC''d before method exits // gets bigger and bigger until heap explodes Collection<String> collection = new ArrayList<String>(); // method never exits normally because of while loop while (true) { collection.add(new String("test")); } } @Override protected void finalize() throws Throwable { System.out.println("Finalizing instance of GcTest"); } }

Se ejecuta con:

Starting Finalizing instance of GcTest Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2760) at java.util.Arrays.copyOf(Arrays.java:2734) at java.util.ArrayList.ensureCapacity(ArrayList.java:167) at java.util.ArrayList.add(ArrayList.java:351) at test.GcTest.main(GcTest.java:22)

Como dije, apenas puedo creerlo, pero no se puede negar la evidencia.

Sin embargo, tiene un sentido perverso, la máquina virtual se habrá dado cuenta de que el objeto nunca se usa y, por lo tanto, se deshace de él. Esto debe ser permitido por la especificación.

Volviendo al código de la pregunta, nunca debe confiar en finalize() para limpiar sus conexiones, siempre debe hacerlo explícitamente.