2012-09-17 19 views
5

Digamos que he creado una cierta clase de recurso con un método close() para limpiar el recurso, y quiero anular finalize() para liberar el recurso (e imprimir una advertencia) si alguien ha olvidado llamar a close(). ¿Cómo se puede hacer esto de forma adecuada?Cómo aplicar correctamente un finalizador para la detección de fugas de recursos en Java

  • ¿Se recomienda solo para recursos nativos (asignados por JNI)?
  • ¿Qué ocurre si se utiliza una referencia a otro objeto que haya sido finalizado, desde un finalizador? Si hay dependencias cíclicas, no veo cómo el recolector de basura puede impedirle acceder a objetos cuyos finalizadores podrían haberse ejecutado.
  • ¿Hay mejores alternativas a primordiales finalize() para detectar y/o hacer frente a las pérdidas de recursos?
  • ¿Alguna otra dificultad a tener en cuenta al implementar un finalizador?

Nota: Sé que usar finalize() suele ser una mala idea, y que no se garantiza que se llame, hay varias otras preguntas sobre esto. Esta pregunta es específicamente sobre cómo para implementar un finalizador en Java, no se trata de qué se debe (o no debería).

+0

Espero que mis respuestas ayuden. Avísame si me perdí algo. –

+0

Se reformuló el segundo punto sobre el acceso a objetos posiblemente finalizados desde un finalizador. Todavía tiene curiosidad sobre cuáles serían las implicaciones de eso. – Soulman

Respuesta

4

En effective java (2nd edition), Joshua va en detalle en el artículo # 7 acerca de cómo se puede hacer esto. Primero sugiere que casi nunca deberías usar finalizer s. Sin embargo, hay una razón para usarlo solo para imprimir una declaración de registro que indique que tiene una fuga de recursos. Él dice que uno de los inconvenientes de hacerlo de esta manera es que alguien puede extender su clase y no llamar correctamente al súper-finalizador. Por lo que sugiere que hacer algo como esto en la subclase:

// Manual finalizer chaining 
    @Override protected void finalize() throws Throwable { 
     try { 
      ... // Finalize subclass state 
     } finally { 
      super.finalize(); 
    } 
} 

Esto es para asegurar que si algo se rompe en la clase actual del finally todavía será llamado. Esta quizás sea una mala solución porque depende de la persona que está subclasificando su clase. Una solución alternativa es usar un archivo de objeto guardián para hacer esto. Esto se ve así:

// Finalizer Guardian idiom 
    public class Foo { 
// Sole purpose of this object is to finalize outer Foo object 
     private final Object finalizerGuardian = new Object() { 
     @Override protected void finalize() throws Throwable { 
      ... // Finalize outer Foo object 
     } 
     }; 
     ... // Remainder omitted 
} 

Este es un enfoque más limpio porque usted sabe que nadie puede anular esa funcionalidad.

La forma sugerido a los recursos de cierre aún está implementando Closeable y asegurarse de que es hasta que el usuario cierre. Como sugiere Josuha, no debes hacer operaciones sensibles al tiempo en el método finalize. La JVM puede elegir ejecutarlo como en algún momento en el futuro. Si depende de este método para hacer commits o algo importante, entonces esa es una mala idea.

1

¿Se recomienda solo para recursos nativos (asignados por JNI)?

No. Su caso de uso también es válido para los finalizadores. Me refiero a la pérdida de recursos de registro.

¿Qué sucede si accede a otro objeto Java que ha sido finalizado, en el finalizador?

Si aún puede acceder a él, no está finalizado. O tal vez me falta algo en tu pregunta.

Entiendo ahora. Es posible que tanto A como B sean elegibles para la recolección de basura y que haya una referencia entre ellos. No debería ser un problema ya que de manera predeterminada finalize() no hace nada. Si escribe para sus objetos personalizados en los métodos finalize(), debe escribir su código independientemente del orden en que se finalicen. También debe proteger la referencia convirtiéndose en null ya que el objeto correspondiente podría haber sido ya recolectado como basura.

¿Hay mejores alternativas para anular finalize() para detectar y/o manejar fugas de recursos?

Creo que lo más importante al utilizar finalizadores es detectar y registrar/advertir sobre la fuga que no se trata. Y el mejor momento para registrar esta fuga es antes de que el objeto de recurso sea basura. Así que los finalizadores son un ajuste natural para esto.

Quiero enfatizar esto: no usaría los finalizadores para tratar con un programador que se olvidó de cerrar el recurso, pero para informarle que debe arreglar su código.

¿Alguna otra dificultad a tener en cuenta al implementar un finalizador?

Parece que también hay una penalización de rendimiento con los objetos que tienen finalizadores. Debes hacer una prueba para ver cómo te va. Si hay una penalización importante en el rendimiento, trataría de imaginar un mecanismo para usar los finalizadores solo en el momento del desarrollo para registrar la fuga de recursos.

Y tenga cuidado al heredar un objeto con finalizadores como lo sugiere Amir Raminfar.

Una cosa más: eche un vistazo al código fuente de [FileInputStream][1] o [FileOutputStream][2] que utilizan finalizadores por el mismo motivo.

+0

"Si todavía puede acceder a él, no está finalizado. O tal vez me falta algo en su pregunta". Pero si el objeto A tiene una referencia al objeto B, y B está finalizado antes que A, entonces ¿no podría A acceder B desde su finalizador? – Soulman

+0

Edité mi publicación y reformulé esa pregunta. – Soulman

+0

Entiendo ahora, actualicé la respuesta. – dcernahoschi

Cuestiones relacionadas