3

我有一些旧代码直到最近才可以使用,但现在它运行在使用 OpenJDK 6 而不是 Java SE 6 的新服务器上似乎很糟糕。

问题似乎围绕着 JAI.create。我有 jpeg 文件,我可以缩放并转换为 png 文件。这段代码过去可以正常工作,但现在已经转移到运行 OpenJDK 的机器上,文件描述符似乎永远不会关闭,而且我看到越来越多的 tmp 文件堆积在服务器上的 tmp 目录中。这些不是我创建的文件,所以我认为是 JAI 做的。

另一个原因可能是新服务器上的堆大小较大。如果 JAI 在 finalize 时进行了清理,但 GC 发生的频率较低,那么文件可能因此而堆积起来。减小堆大小不是一种选择,而且我们似乎遇到了与增加 ulimit 无关的问题。

这是我运行此文件时泄漏的文件示例:

/tmp/imageio7201901174018490724.tmp

一些代码:

// Processor is an internal class that aggregates operations
// performed on the image, like resizing
private byte[] processImage(Processor processor, InputStream stream) {
    byte[] bytes = null;
    SeekableStream s = null;
    try {
        // Read the file from the stream
        s = SeekableStream.wrapInputStream(stream, true);
        RenderedImage image = JAI.create("stream", s);
        BufferedImage img = PlanarImage.wrapRenderedImage(image).getAsBufferedImage();
        // Process image
        if (processor != null) {
            image = processor.process(img);
        }
        // Convert to bytes
        bytes = convertToPngBytes(image);
    } catch (Exception e){
       // error handling
    } finally  {
        // Clean up streams
        IOUtils.closeQuietly(stream);
        IOUtils.closeQuietly(s);
    }
    return bytes;
}

private static byte[] convertToPngBytes(RenderedImage image) throws IOException {
    ByteArrayOutputStream out = null;
    byte[] bytes = null;
    try {
        out = new ByteArrayOutputStream();
        ImageIO.write(image, "png", out);
        bytes = out.toByteArray();
    } finally {
        IOUtils.closeQuietly(out);
    }
    return bytes;
}

我的问题是:

  1. 有没有人遇到这个并解决了它?由于创建的 tmp 文件不是我的,我不知道它们的名称是什么,因此无法对它们做任何事情。
  2. 有哪些选择用于调整图像大小和重新格式化图像的库?我听说过 Scalr - 还有什么我应该研究的吗?

我宁愿此时不重写旧代码,但如果没有其他选择......

谢谢!

4

2 回答 2

4

只是对临时文件/终结器问题的评论,现在您似乎已经解决了问题的根源(评论太长了,所以我将其作为答案发布...... :-P):

临时文件由 ImageIO 的FileCacheImageInputStream. ImageIO.createImageInputStream(stream)每当您调用并且useCache标志是true(默认值)时,都会创建这些实例。您可以将其设置false为禁用磁盘缓存,代价是内存缓存。这可能是有意义的,因为您有一个大堆,但如果您正在处理非常大的图像,则可能不是。

我也认为您(几乎)对终结器问题是正确的。您将在FileCacheImageInputStream(Sun JDK 6/1.6.0_26) 上找到以下“finalize”方法:

protected void finalize() throws Throwable {
    // Empty finalizer: for performance reasons we instead use the
    // Disposer mechanism for ensuring that the underlying
    // RandomAccessFile is closed/deleted prior to garbage collection
}

在类的构造函数中有一些非常“有趣”的代码,当实例完成时设​​置自动流关闭和处理(如果客户端代码忘记这样做)。这在 OpenJDK 的实现中可能有所不同,至少看起来有点 hacky。目前我还不清楚我们所说的“性能原因”究竟是什么......

无论如何,就像您现在所做的那样,似乎调用close实例ImageInputStream将正确关闭文件描述符并删除临时文件。

于 2013-09-04T11:14:07.220 回答
0

找到了!

因此,一个流被代码中不同区域的另一个流包裹:

iis = ImageIO.createImageInputStream(stream);

再往下,流是关闭的。

这在使用 Sun Java 运行时似乎不会泄漏任何资源,但在使用 Open JDK 运行时似乎会导致泄漏。

我不确定为什么会这样(我没有查看源代码来验证,尽管我有我的猜测),但这似乎正在发生。一旦我明确关闭包装流,一切都很好。

于 2013-09-03T19:37:19.097 回答