java memory-management freemarker stringwriter

java - ¿El cierre de un guionista no causa una fuga?



memory-management freemarker (4)

Al final del método no queda ninguna referencia para el writer por lo tanto, será liberado por el GC.

Me doy cuenta de que en Java el GC eventualmente limpiará los objetos, pero le pregunto si es una mala práctica no cerrar el escritor de cadenas, actualmente estoy haciendo esto:

private static String processTemplate(final Template template, final Map root) { StringWriter writer = new StringWriter(); try { template.process(root, writer); } catch (TemplateException e) { logger.error(e.getMessage()); } catch (IOException e) { logger.error(e.getMessage()); } finally { } return writer.toString(); }

Debería estar cerrando el escritor y creando una nueva cadena como esta:

String result = ""; ... finally { result = writer.toString(); writer.close(); }

¿Es mejor hacer esto?


El javadoc es bastante explícito:

Cerrar un StringWriter no tiene efecto.

Y un rápido vistazo al código lo confirma:

public void close() throws IOException { }


No tiene ningún recurso que no sea de memoria. Será basura recolectada como cualquier otra cosa. La probabilidad de cerrar () simplemente existe porque otros objetos de escritura contienen recursos que deben limpiarse, y se necesita el cierre () para satisfacer la interfaz.


No, no cerrar un StringWriter no causará una fuga: como se señaló, StringWriter#close() es un nop, y el escritor solo guarda memoria, no recursos externos, por lo que estos se recopilarán cuando se recopile el escritor. (Explícitamente, contiene referencias a objetos en campos privados que no escapan al objeto, concretamente a StringBuffer , por lo que no hay referencias externas).

Además, generalmente no debes cerrar un StringWriter , ya que agrega repetitivo a tu código, ocultando la lógica principal, como veremos. Sin embargo, para tranquilizar a los lectores de que estás siendo cuidadoso y haces esto intencionalmente, recomiendo comentar este hecho:

// Don''t need to close StringWriter, since no external resource. Writer writer = new StringWriter(); // Do something with writer.

Si desea cerrar el escritor, lo más elegante es usar try-with-resources , que llamará automáticamente close() cuando salga del cuerpo del bloque try:

try (Writer writer = new StringWriter()) { // Do something with writer. return writer.toString(); }

Sin embargo, dado que Writer#close() lanza la IOException , su método ahora también necesita lanzar la IOException aunque nunca ocurra , o si necesita capturarlo, para demostrarle al compilador que se maneja. Esto es bastante complicado:

Writer writer = new StringWriter(); try { // Do something with writer, which may or may not throw IOException. return writer.toString(); } finally { try { writer.close(); } catch (IOException e) { throw new AssertionError("StringWriter#close() should not throw IOException", e); } }

Este nivel de repetición es necesario porque no puede limitarse al bloqueo de prueba general, ya que, de lo contrario, podría tragarse accidentalmente una IOException del cuerpo de su código. Incluso si no hay ninguno actualmente, algunos podrían agregarse en el futuro y querría que el compilador lo advierta. AssertionError está documentando el comportamiento actual de StringWriter#close() , que podría cambiar potencialmente en una versión futura, aunque eso es extremadamente improbable; también oculta cualquier excepción que pueda ocurrir en el cuerpo del intento (de nuevo, esto nunca debería ocurrir en la práctica). Esto es demasiado complicado y complejo, y claramente estaría mejor omitiendo el close() y comentando por qué.

Un punto sutil es que Writer#close() no solo lanza una IOException , sino también StringWriter#close() , por lo que no puede eliminar la excepción al hacer que la variable sea StringWriter lugar de Writer . ¡Esto es diferente de String Reader , que anula el método close() y especifica que no lanza una excepción! Ver mi respuesta a ¿Debo cerrar un StringReader? . Esto puede parecer incorrecto. ¿Por qué tendría un método que no hace nada pero puede lanzar una excepción? - pero presumiblemente es para compatibilidad hacia adelante, para dejar abierta la posibilidad de lanzar una IOException en el futuro, ya que este es un problema para los escritores en general. (También podría ser un error).

Para resumir: está bien no cerrar un StringWriter , pero la razón para no hacer lo correcto de manera habitual, es decir, intentar con recursos, es simplemente porque close() declara que lanza una excepción que en realidad no lanza en la práctica. , y el manejo de esto precisamente es un montón de repetitivo. En cualquier otro caso, es mejor usar el patrón de administración de recursos convencionalmente correcto y evitar problemas y rascarse la cabeza.