JAI创建似乎打开了文件描述符

时间:2013-08-29 17:33:31

标签: java memory jai resource-leak

我有一些旧的代码直到最近一直在运行,但现在看来它是在使用OpenJDK 6而不是Java SE 6的新服务器上运行。

问题似乎围绕着JAI.create。我有jpeg文件,我可以缩放并转换为png文件。此代码过去没有泄漏,但现在已经移动到运行OpenJDK的盒子,文件描述符似乎永远不会关闭,我看到越来越多的tmp文件在服务器上的tmp目录中累积。这些不是我创建的文件,所以我认为是JAI就是这样做的。

另一个原因可能是新服务器上的堆大小较大。如果JAI在最终确定时清理,但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 - 我还应该研究什么?
  3. 我不想在此时重写旧代码,但如果没有其他选择......

    谢谢!

2 个答案:

答案 0 :(得分: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,正如您现在所做的那样,将正确关闭文件描述符并删除临时文件。

答案 1 :(得分:0)

发现它!

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

iis = ImageIO.createImageInputStream(stream);

再向下,流已关闭。

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

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