不关闭字符串编写器会导致泄漏吗?

问题描述 投票:0回答:5

我意识到在java中GC最终会清理对象,但我问不关闭字符串编写器是否是不好的做法,目前我正在这样做:

 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();
    }

我应该关闭编写器并创建一个像这样的新字符串吗:

String result = "";

...

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

这样做更好吗?

java memory-management freemarker stringwriter
5个回答
92
投票

javadoc非常明确:

关闭 StringWriter 没有任何效果。

快速浏览一下代码证实了这一点:

public void close() throws IOException {
}

6
投票

它不持有任何非内存资源。 它将像其他东西一样被垃圾收集。 close() 可能仅仅存在,因为其他编写器对象确实持有需要清理的资源,并且需要 close() 来满足接口。


4
投票

不,不关闭

StringWriter
不会导致泄漏:如前所述,
StringWriter#close()
是一个 nop,writer 只保存内存,不保存外部资源,所以这些将在 writer 被收集时被收集。 (明确地说,它保存对私有字段中不转义对象的对象的引用,具体来说是
StringBuffer
,因此没有外部引用。)

此外,您通常不应该关闭StringWriter

,因为它会向您的代码添加样板,从而模糊主要逻辑,正如我们将看到的。然而,为了让读者放心,您是很小心并且是故意这样做的,我建议评论这个事实:

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

如果你确实想关闭 writer,最优雅的方法是使用

try-with-resources,当你退出 try 块的主体时,它将自动调用 close()

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

但是,由于

Writer#close() 会抛出 IOException

,因此您的方法现在还需要抛出 
IOException
 
,即使它从未发生过,或者您需要捕获它,以向编译器证明它已被处理。这很复杂:

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); } }

这种级别的样板是必要的,因为你不能只在整个 try 块上放置一个 catch,否则你可能会意外吞下代码主体抛出的

IOException

。即使当前没有,将来也可能会添加一些,并且您希望编译器对此发出警告。 
AssertionError
 正在记录 
StringWriter#close()
 的当前行为,这可能会在未来的版本中发生变化,尽管这种情况极不可能发生;它还掩盖了尝试主体中可能发生的任何异常(同样,这在实践中永远不应该发生)。这是太多的样板和复杂性,你显然最好省略 
close()
 并评论原因。

一个微妙的点是,不仅

Writer#close()

会抛出
IOException
,而且
StringWriter#close()
也会抛出,因此你无法通过将变量设置为
StringWriter
而不是
Writer
来消除异常。这与 String
Reader 不同,后者重写了 close() 方法并指定它
 抛出异常!请参阅
我对我应该关闭 StringReader 吗?的回答。这可能看起来是错误的——为什么你会有一个不执行任何操作但可能抛出异常的方法? – 但大概是为了向前兼容,保留将来在关闭时抛出 IOException 的可能性,因为这对作家来说通常是一个问题。 (这也可能只是一个错误。)
总结一下:不关闭 
StringWriter

很好,但是不做通常正确的事情(即 try-with-resources)的原因只是因为

close()

 声明它抛出了一个实际上并没有抛出的异常在实践中,准确地处理这个问题需要大量的样板文件。在任何其他情况下,最好只使用传统上正确的资源管理模式并防止出现问题和令人头疼的事情。

在该方法结束时,

0
投票
不再有任何引用,因此它将被 GC 释放。


我认为您应该关闭它,原因如下:

0
投票

如果不关闭它,耦合性将会增加,因为您的代码严重依赖于实现细节。如果将来 close 的实现发生变化,您的代码很可能将不再工作。
  1. StringWriter 实现了 closeable 接口。可关闭接口的语义规定,开发人员应该在不再需要资源时关闭该资源,以避免内存泄漏。就 StringWriter 而言,该类的设计从一开始就是完全错误的,因为 close 没有执行任何操作。为了保留接口的语义,我认为无论如何都应该关闭它。
© www.soinside.com 2019 - 2024. All rights reserved.