JDK 1.7:"太多打开的文件"由于POSIX信号量?

时间:2012-05-04 00:45:30

标签: java file-io java-7

我已经查看过关于SO的其他类似问题,但它们似乎是由其他问题引起的。

首先,我确保我明智地关闭了所有文件句柄,然后使用lsof -p <pid of java>查看了我的文件列表。

它在整个运行时间内保持相当稳定但是我会定期在lsof中列出大约10,000个条目,如下所示:

COMMAND   PID USER   FD     TYPE DEVICE  SIZE/OFF     NODE NAME
                                      ...
java    36809  smm *235r  PSXSEM              0t0          kcms00008FC901624000
java    36809  smm *236r  PSXSEM              0t0          kcms00008FC901624000
java    36809  smm *237r  PSXSEM              0t0          kcms00008FC901624000
java    36809  smm *238r  PSXSEM              0t0          kcms00008FC901624000
java    36809  smm *239r  PSXSEM              0t0          kcms00008FC901624000

手册页说PSXSEM类型是POSIX信号量。 JDK使用POSIX信号量的任何线索?顺便说一句,该应用程序目前是一个单线程命令行应用程序。

潜在有用的背景:我在Mac OS X 10.7.3上升级到JDK 1.7后首次注意到这一点:

java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)

更新:在JDK 1.6上重新发布$JAVA_HOME似乎是此问题的解决方法。

java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04-415-11M3635)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01-415, mixed mode)

JDK 1.7的做法有何不同?

3 个答案:

答案 0 :(得分:7)

我能够将其追溯到这段代码:

BufferedImage image = null;
ImageInputStream stream = null;
try {
    stream = new FileImageInputStream(file);
    image = ImageIO.read(stream);

} catch (Exception ex) {
    log.error("Image could not be read: "+file.getPath());

} finally {
    // ImageIO closes input stream unless null is returned
    // http://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html#read(javax.imageio.stream.ImageInputStream)
    if (stream != null && image == null) {
        try {
            stream.close();
        } catch (IOException ex) {
            log.error("ERROR closing image input stream: "+ex.getMessage(), ex);
        }
    }
}

JavaDocs明确指出此方法(与其他方法不同)会自动关闭流。实际上,当您尝试手动关闭它时,它会抛出一个说“关闭”的异常。我正在使用这个重载,因为另一个说它无论如何都将它包装在ImageInputStream中所以我认为我会节省一些工作。

更改块以使用普通FileInputStream修复泄漏:

BufferedImage image = null;
InputStream stream = null;
try {
    stream = new FileInputStream(file);
    image = ImageIO.read(stream);

} catch (Exception ex) {
    log.error("Image could not be read: "+file);

} finally {
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException ex) {
            log.error("ERROR closing image input stream: "+ex.getMessage(), ex);
        }
    }
}

在我看来这是JDK 1.7中的一个错误,因为1.6在这里运行良好。

更新:对于此问题我只是submitted a bug report到Oracle。

答案 1 :(得分:4)

我找到了另一个原因。似乎ColorSpace的toRGB()方法正在泄漏信号量。运行以下代码:

import java.awt.color.ColorSpace;
public class Test
{
    public static void main(String[] args) throws Throwable {
        final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
        for(int i = 0; i < 10000000; i++) {
            CIEXYZ.toRGB(new float[] {80f, 100, 100});
        }
    }
}

使用:

java version "1.7.0_04"
Java(TM) SE Runtime Environment (build 1.7.0_04-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.0-b21, mixed mode)

将使您离开系统文件。

编辑已经向Oracle提交了bug report

答案 2 :(得分:4)

更新:正如其他用户所说,ImageIO在1.7.0_04和1.7.0_05泄漏了信号量。用户juancn和用户mckamey的错误报告已被标记为已修复且已关闭(感谢大家!)。解释:

  

此修复报告macosx上的文件处理程序泄漏。它提到了两种泄漏处理程序的方法:    通过ImageIO.read(ImageInputStream)和通过信号量。

     

我没有观察到第一次泄漏:如果找到合适的阅读器,我们会明确关闭输入流,这足以(至少在1.7.4上)释放文件句柄。

     

然而,在信号量的情况下,我们泄漏了大量的句柄:我们执行颜色转换    对于jpeg图像的每一行,每次我们创建一个信号量(因为我们看到2个或更多    CPU安装在系统上),然后我们将单独任务的数量减少到1(因为    我们有单个扫描线来处理),因此从不取消链接信号量。

     

Linux系统上存在同样的问题,但程度较低,因为我们占用单一的问题    每个命名信号量的文件句柄,而在macosx上我们总是占用新的文件句柄。

     

建议修复只是推迟创建命名信号量,直到我们澄清数量    因此,现在我们不创建用于图像读取和简单颜色的信号量    转换(如ColorSpace.toRGB())。除此之外,现在我们使用pSem指针作为触发器    对于信号量的破坏。

即使他们的报告显示该修补程序是在版本8中,但是一个backport报告表明它是fixed in 1.7.0_06.

因此,如果你在1.7.0_04或05上看到这个,那么更新到至少1.7.0_06将解决这个问题。