【问题标题】:JAI create seems to leave file descriptors openJAI create 似乎使文件描述符保持打开状态
【发布时间】:2013-08-29 17:33:31
【问题描述】:

我有一些旧代码直到最近才可以使用,但现在它运行在使用 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 - 还有什么我应该研究的吗?

此时我宁愿不重写旧代码,但如果没有别的选择……

谢谢!

【问题讨论】:

    标签: java memory jai resource-leak


    【解决方案1】:

    只是对临时文件/终结器问题的评论,现在您似乎已经解决了问题的根源(评论太长了,所以我将它作为答案发布...... :-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。目前我还不清楚我们所说的“性能原因”究竟是什么......

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

    【讨论】:

    • 感谢您的意见。我承认我没有深入研究源代码。我也不知道缓存设置。非常感谢您的反馈。
    • 肯定发生了一些奇怪的事情,在我的例子中,JNA 过早地关闭了句柄。奇怪stackoverflow.com/q/38444752/32453
    【解决方案2】:

    找到了!

    所以一个流被代码中不同区域的另一个流包裹:

    iis = ImageIO.createImageInputStream(stream);
    

    再往下,流是关闭的。

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

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

    【讨论】:

    • 所以在这种情况下,您明确地调用了iis.close() 并解决了问题?
    • @rogerdpack 唉,这是很久以前的事了,我不记得我做了什么。我同意这似乎是答案所暗示的,我只是无法验证这是否确实是我所做的。对不起。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-31
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多