不关闭 stringwriter 会导致泄漏吗?

Posted

技术标签:

【中文标题】不关闭 stringwriter 会导致泄漏吗?【英文标题】:Will not closing a stringwriter cause a leak? 【发布时间】:2013-01-10 15:22:16 【问题描述】:

我意识到在 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();

这样做更好吗?

【问题讨论】:

另见StringReader的相应问题:Should I close a StringReader?。 【参考方案1】:

javadoc 非常明确:

关闭 StringWriter 无效。

快速查看the code 即可确认:

public void close() throws IOException 

【讨论】:

close() 什么都不做并抛出 IOException :) 它不会抛出 IOException。它表明它可以抛出异常,但它不会。【参考方案2】:

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

【讨论】:

【参考方案3】:

不,不关闭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.

如果你确实想关闭编写器,最优雅的方法是使用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() 的当前行为,这可能会在未来的版本中发生变化,尽管这种可能性极小;它还掩盖了 try 主体中可能发生的任何异常(同样,这在实践中绝不应该发生)。这是太多的样板和复杂性,您显然最好省略close() 并评论原因。

微妙的一点是,Writer#close() 不仅会抛出 IOExceptionStringWriter#close() 也会抛出异常,因此您无法通过将变量设置为 StringWriter 而不是 Writer 来消除异常。这与 StringReader不同,后者重写 close() 方法并指定它抛出异常!请参阅 my answer 至 Should I close a StringReader?。这看起来可能是错误的——为什么你会有一个什么都不做但可能抛出异常的方法? – 但可能是为了向前兼容,以便将来关闭IOException 的可能性保持开放,因为这对于作家来说是一个普遍的问题。 (也可能只是一个错误。)

总结一下:不关闭StringWriter 很好,但不做通常正确的事情的原因,即try-with-resources,只是因为close() 声明它会抛出它没有的异常' t 实际上投入实践,并且精确地处理这个是很多样板。在任何其他情况下,最好只使用传统上正确的资源管理模式并防止出现问题和令人头疼的问题。

【讨论】:

【参考方案4】:

在方法结束时,没有对 writer 的引用,因此它将被 GC 释放。

【讨论】:

以上是关于不关闭 stringwriter 会导致泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 关闭小部件时 BLoC 流实例会导致内存泄漏吗?

不关闭我的 JDBC PreparedStatements 是不是会导致内存泄漏?

迭代器会导致内存泄漏吗?

从未解决的承诺会导致内存泄漏吗?

在静态方法中使用匿名 Lamba 订阅事件会导致内存泄漏吗?

此 CAAnimation 会导致泄漏或保留周期吗?