【问题标题】:Will not closing a stringwriter cause a leak?不关闭 stringwriter 会导致泄漏吗?
【发布时间】: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();
}

这样做更好吗?

【问题讨论】:

标签: java memory-management freemarker stringwriter


【解决方案1】:

不,不关闭 StringWriter 不会导致泄漏:如前所述,StringWriter#close() 是一个 nop,写入器只保存内存,而不是外部资源,所以这些将在写入器被收集时被收集。 (明确地说,它包含对不转义对象的私有字段中的对象的引用,具体来说是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() 的当前行为,这可能会在未来的版本中发生变化,尽管这种可能性极小;它还掩盖了 try 主体中可能发生的任何异常(同样,这在实践中绝不应该发生)。这是太多的样板和复杂性,您最好省略close() 并评论原因。

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      javadoc 非常明确:

      关闭 StringWriter 无效。

      快速查看the code 即可确认:

      public void close() throws IOException {
      }
      

      【讨论】:

      • close() 什么都不做并抛出 IOException :)
      • 它不会抛出 IOException。它表明它可以抛出异常而它不会。
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-05
        • 1970-01-01
        • 2019-02-03
        • 1970-01-01
        • 2014-05-18
        • 2022-06-13
        • 1970-01-01
        相关资源
        最近更新 更多