Contenido de certificación
Buscar
Social
Ofertas laborales ES
« Preguntas para javeros curiosos: devolución de resultados en métodos | Main | Escalabilidad multihilo de la clase ConcurrentHashMap »
jueves
oct112012

Pregunta de nota para Javeros y JDBCianos.

El problema es el siguiente:

"Un desarrollador crea un programa java con una conexión jdbc (instancia de java.sql.Connection que llama conn), realizar una llamada a un procedimiento (instancia de java.sql.CallableStatement que llama cstmt), recorre los datos recibidos (instancia de java.sql.ResultSet que llama rs), y después de esto, libera recursos, de la siguiente manera:

  • rs.close();
  • cstmt.close();    
  • rs.close();     "

La pregunta es la siguiente:

 "¿Es conveniente que este desarrollador asigne a nulas estas variables que, aparentemente, se han cerrado correctamente? "

Veamos las respuestas de los lectores....

Reader Comments (20)

Este tipo de cosas debería ir al foro y no a la sección de noticias....

octubre 11, 2012 | Unregistered CommenterFran

Con una versión modenilla de java, yo no lo haría (siempre que el scope de las variables esté razonablemente elegido).

octubre 11, 2012 | Unregistered Commenterhector

Buenos días,

Debo entender que se trata de "un problema" para realizar un producto extremadamente óptimo. Al asignar valor null a las variables, entiendo yo, que facilitas que estas sean recogidas y tratadas por el recolector de basura, por lo que podría ser en cierta manera más eficiente. Si lo que buscas es liberar la memoria que puedan ocupar estas variables, podrías asignarlas a null y llamar al recolector de basura, y que este se ejecute en primer momento que el estime oportuno.

Por otro lado, veo que haces un close por segunda vez del ResultSet (rs.close), en el caso que lo que pretendas hacer sea un close de la conexión (conn.close()), has de tener en cuenta la transacción, ya que si en el procedimiento no se hace commit o rollback y tu tampoco, es como si no se hubiera hecho nada, y todos los cambios que este (el procedimiento) pudiera haber hecho no se verán reflejados en la BBDD.

Saludos.

octubre 11, 2012 | Unregistered Commenterjose

Para empezar, ni siquiera están bien cerradas, "aparentemente" ;)

octubre 11, 2012 | Unregistered CommenterKomorr

No hay necesidad de asignar valores null a las variables.
Si son locales, el GC recuperará la memoria que hayan usado, una vez que el método finalice.
Si son globales, dependerá de que la instancia deje de estar referenciada por cualquier otro objeto, para que el GC recupere la memoria.
Asignar valores null no garantiza que se recupere la memoria sin más.

octubre 11, 2012 | Registered Commenterchoces

Yo también quiero hacer un par de preguntas:

¿Siguen haciendo falta 10 votos para aprobar la publicación de una noticia?
¿Le pasa algo a los foros?

octubre 11, 2012 | Unregistered Commenterestadisticas

Buen apunte @choces, la pregunta entonces es la siguiente:
"¿Eso aseguraría que, en todos los casos, las conexiones JDBC se cerrarán correctamente, o pueden ocurrir situaciones en las que, a pesar de este código, queden conexiones abiertas que nos pueda producir un uso deficiente del servidor?" "En tal caso, ¿cómo podríamos minimizar esa casuística?"

octubre 11, 2012 | Registered Commenterjcarmonaloeches

El cierre de las conexiones, o de los recursos, es otra cuestión diferente, que no tiene que ver con las asignaciones de las variables, ni con la actividad del GC.
Los recursos JDBC se deben cerrar, una vez que se hayan usado; lo mismo vale para las conexiones, a menos que se desee que permanezcan abiertas (éste es otro complejo problema).
Si esos recursos no se cierran, irán a una zona del Heap (Old Generation), donde son inalcanzables para el GC (se mantienen referencias a ellos), por lo que son una fuente de memory leak, al margen de lo que se haga con las variables.

octubre 11, 2012 | Registered Commenterchoces

Para estar seguro y salvar posibles excepciones, hay que meterlo en un bloque "try" y dependiendo de si se quiere controlar la excepcion o no en ese punto meter el "catch", pero en todo caso meter un "finally" que garantizaría el cierre de la conexion.


Connnection conn;
CallableStatement cstmt;
Resultset rs;
try{
.... [ejecutar consulta y procesar resultados]
}finally{
try{rs.close();}catch(Exception ex1) { [tratar ex1 al cerrar rs]}
try{cstmt.close();}catch(Exception ex2) { [tratar ex2 al cerrar cstmt]}
try{conn.close();}catch(Exception ex3) { [tratar ex3 al cerrar conn]}
}

octubre 11, 2012 | Unregistered CommenterVgimeno

Algunos analizadores estaticos como findbug te recomendaran que antes de hacer el cierre de la conexion realizes una verificacion de que la variable sea distinta de null
if (rs !=null) {
rs.close();
}
y por supuesto concuerdo que si la variables fueron declaradas locales no es necesario asignarles null.

octubre 11, 2012 | Unregistered CommenterReynaldo Quiroz Sapiencia

Perfectamente explicado, poco que añadir man :)

octubre 11, 2012 | Registered Commenterjcarmonaloeches

esto no es una noticia ni aporte que valga la pena poner en la portada

octubre 11, 2012 | Unregistered Commenteralexis

De hecho, si no me equivoco, esa instrucción fallaría en el segundo rs.close(), ya que te diría que el resultset ya está cerrado.

Además, si cierras el cstm ya se cierra automáticamente el resultset.

octubre 11, 2012 | Unregistered Commentersamuelgmartinez

Si esos recursos no se cierran, irán a una zona del Heap (Old Generation), donde son inalcanzables para el GC (se mantienen referencias a ellos), por lo que son una fuente de memory leak, al margen de lo que se haga con las variables.

Y eventualmente el fantasma del "OutOfMemory: PermGen" vendrá a jalarte los pies por las noches...

octubre 11, 2012 | Registered Commenterantoniovl

Una nota al margen, referente a las conexiones.

Lo usual es usar un connection pool en servidores, con el objeto de reutilizar las conexiones que ya no están activas. El DriverManager consume tiempo, por lo que el pool es más eficiente. Podría ser similar a usar un Executor con respecto a los threads.
Cuando se "cierra" una conexión asociada a un pool, en realidad se marca como "disponible", y se ahorra la necesidad de recurrir al DriverManager (mientras haya suficientes disponibles en el pool).
En todo caso, es necesario cerrar la conexión, si se quiere que el pool sea lo más eficiente posible.

En aplicaciones de escritorio, que usen una base de datos embebida, normalmente no se usa un pool, por innecesario.

octubre 11, 2012 | Registered Commenterchoces

¿Alguien se ha mirado el código? Para empezar, el código está mal, ya que en vez de cerrar la conexión, se cierra dos veces el ResultSet. Además, dado que cerrar resultsets y statements puede lanzar excepciones, ese código no garantiza para nada que se cierren todos los objetos correctamente.

Y puestos a puntualizar: Dos cosas:
1) El Old Heap no es una zona de memoria inalcanzable por el GC. El Old Heap se libera cuando se hace un GC completo, y si los objetos no se liberan, no es por que "vayan allí": no se liberan por que el no cerrar adecuadamente el recurso, quedan "enlaces vivos" a esas referencias y por lo tanto el GC no los puede liberar. No por que estén en una zona de memoria especial.

2) La zona de memoria PermGen (por cierto que desaparecerá como tal en Java 8), es una zona donde se almacenan cosas que se suponen persistentes, como definiciones de clases, elementos estáticos etc. Así que un memory leak de objetos no causará un OOM PermGen, causará un OOM "normal y corriente". Los errores de falta de PermGen son de otro tipo y suelen estar relacionados con creación excesiva de clases (scripting, JSPs...) y leaks asociados a classloaders.

Volviendo al tema original, en versiones antiguas de Java había algunos casos excepcionales donde asignar una variable a null ayudaba ligeramente al GC, pero eso hace tiempo que está obsoleto y lo único que hace es "polucionar" el código.

octubre 11, 2012 | Unregistered CommenterKomorr

Les dejo dos link que me ayudaron cuando me ocurrió lo que se describe. Si bien es parecido ya que uso jdbc - mysql la lectura puede ayudar a comprender algunas cuestiones..

memory leak

bug

Saludos.

octubre 14, 2012 | Unregistered Commenterdani

"Si esos recursos no se cierran, irán a una zona del Heap (Old Generation), donde son inalcanzables para el GC (se mantienen referencias a ellos)..."

El contenido del paréntesis explica la razón por la que son inalcanzables. Tal vez se preste a confusión, por la manera como está redactado; pero no se pretendía decir que los objetos en Old Generation son inalcanzables por estar en esa región.

octubre 15, 2012 | Registered Commenterchoces

Lo de "donde son inalcanzables" es lo que se presta a confusión, junto con lo de "inalcanzables"... Ya que en realidad el dónde no importa y alcanzarlos, el GC los alcanza, lo que pasa es que, por lo que que pones entre paréntesis, no los puede reclamar.
Hay tanta gente confundida con el tema que prefería puntualizar por si acaso alguno lo entendía mal.

octubre 15, 2012 | Unregistered CommenterKomorr

Para los que estáis discutiendo sobre la recolección de basura, os dejo un concepto que veo que no estáis teniendo en cuenta cuando habláis de "referencias": "Island of Isolation".

https://www.google.es/search?q=island+of+isolation+java

Java no utiliza conteo de referencias para realizar el GC. Eso lo utilizan otros lenguajes como c++ (Boost framework), Python, Objective C, etc.

El hecho de que un grupo de objetos mantenga referencias entre ellos (un árbol, una lista doblemente enlazada) no tiene nada que ver para que se genere un memory leak en java. Es más importante la referencia raíz(hard o soft reference) que el número de referencias. Es decir, que se haga referencia, directa o indirectamente, a alguno de esos elementos (o a su conjunto total) desde algun thread concreto.

Y otra cosa interesante es que ninguna de las generaciones es inalcanzable para el GC. Hasta la PermGen (donde exista, ya que la IBM JVM no la tiene) es objetivo del GC, ya que hay mecanismos de "class unloading" cuando no hay "necesidad" de estas. Hay mil demostraciones de esto.

https://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation

Los leaks que genera el driver de JDBC (el de MySQL por ejemplo) es que cuando se inicializa levanta un hilo de gestión a mayores, donde almacena sus propias cachés y demás.

octubre 16, 2012 | Registered Commentersamuelgmartinez

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>