lunes, 2 de marzo de 2009

Los finalizers no son tus amigos

Gracias al artículo de este mes de javaspecialists.eu (http://www.javaspecialists.eu/archive/Issue170.html), he redescubierto que hay que evitar la implementación del método finalize() siempre que sea posible.

¿Por qué? Porque se añaden handlers a todas y cada una de las intancias de una clase que tenga ese método implementado. ¿Y qué? pues por un lado se añade más footprint de memoria a cada objeto, pero lo más importante: porque un objeto con finalizador es más costoso de alojar, y más costoso de eliminar: la VM añade un hook al objeto que causa que la instancia "sobreviva" a muchos de los pasos de la recolección de basura en lugar de ser retirado rápidamente, por lo que se promociona de forma prematura (y posiblemente innecesaria) dicha instancia a la zona de "old generation". Y esto a su vez hace que el old generation GC tenga más trabajo y se haga más lento y consuma más CPU. Según se apunta por ahí, esta situación puede mejorarse tuneando los ratios de tamaños, pero aún así el coste es importante.

Pero... -te estarás diciendo- si yo necesito un finalizador para cerrar un stream!!! (o cualquier otro recurso). Bueno, puede que sí, puede que no. Revísalo ¿seguro? ¿o lo haces por comodidad? ¿o porque no te fías de que la VM haga lo que tiene que hacer? ¿o porque quieres ayudar al GC (y la cagas, como siempre)? :-)

Si sigues estando seguro de que lo necesitas, dos comentarios:
  1. Ok, pues no pasa nada. No he dicho que no uses finalizadores, sólo que los uses cuando sea absolutamente necesario. Ten en cuenta que este tip es de un nivel muy "fino". Si ese objeto no afecta al rendimiento global por no ser uno de los más utilizados ni entrar en hotspots, pues no te preocupes, recuerda la regla del 90-10.
  2. Si necesitas tunear un caso concreto, y no has visto la forma de simpliciarlo reordenando otras partes del código, siempre puede reemplazarse un método finalize por un establecimiento de una referencia débil (weak) al objeto, y llamando explícitamente aun método equivalente que libera recursos cuando dicha referencia se establece a null.
Nota: esto es así siempre que el finalizador sea "no trivial" (entendiendo por trivial = vacío en tiempo de ejecución) es decir que si dejamos el método en nuestra clase pero su cuerpo está vacío, o si el optimizador del Hotspot mediante inlining lo vacía ("if (false) {...}"), lo dicho anteriormente no se considera: sólo se añaden hooks a objetos con finalizadores no triviales.

Más información:

Ahora repite conmigo:
Los finalizers no son mis amigos.... Finalizer malo, finalizer malo...

[Continúa en el siguiente post: Los finalizers no son tus amigos (parte II)]

2 comentarios:

angelborroy dijo...

Juajua...

Justamente hoy he leído un informe que le han pasado mi cliente acerca de "Codificación Segura en Java" elaborado por una prestigiosa empresa de seguridad vasca y decía que todos los objetos deben implementar el método finalize(). Casi me he petado al leerlo, aunque ahora (datos en mano) no sé si meter cizaña para que los "expertitos" sean puestos en su sitio :-P

serverperformance dijo...

Pues fuera de guasas, párales los pies, que es el típico gato muerto que emparedas y luego no sabes dónde coño está...

A NO SER QUE LA ESCALABILIDAD DE ESA APLICACIÓN NO IMPORTE PARA NADA...

Por cierto, sólo te faltaba decir que empieza por consonante y diete dos dígitos en el nombre...