【问题标题】:JDK 1.7: "Too many open files" due to POSIX Semaphores?JDK 1.7:由于 POSIX 信号量,“打开的文件太多”?
【发布时间】:2013-05-22 07:39:34
【问题描述】:

我查看了关于 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 有何不同?

【问题讨论】:

  • 我会尝试使用常规的 Java 分析器,例如 YourTrack 甚至只是 VisualVM,看看您是否可以将 10K 信号量的创建与大量 Java 库对象的创建关联起来。
  • 我仍然看到这个问题,但我没有使用 ImageIO(至少不是直接使用)。重绘只会导致信号量的数量增加,直到我得到:2012-05-09 16:30:12.856 java[14407:3d87] Persistent UI failed to open file file://localhost/Users/juancn/Library/Saved %20Application%20State/net.java.openjdk.cmd.savedState/window_1.data:打开的文件太多(24)

标签: java file-io java-7


【解决方案1】:

我能够追溯到这段代码:

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.7.0_04 开始,它似乎没有得到修复,尽管规范中没有其他 API 允许您检查图像的有效性,但这太糟糕了。但是,我想你很幸运,我仍然让那些信号量在杀死 JEE 容器时挥之不去。
  • @ArchimedesTrajano 即使解决了这个问题,我仍然会遇到间歇性问题。似乎是一个非常糟糕的错误。
  • 仅适用于 JPEG 文件。因为我只用来检查 JPEG 文件是否“有效”,所以我只检查它是否在正确的位置有图像开始和图像结束。
  • 首先感谢您的关注和修复!其次,您是否有机会指出我如何使用您的修复程序从 url 读取图像?
  • 你试过URL.openStream()吗?
【解决方案2】:

我找到了另一个原因。似乎 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

【讨论】:

    【解决方案3】:

    更新: 正如其他用户所说,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 中,反向移植报告表明它是 fixed in 1.7.0_06.

    因此,如果您在 1.7.0_04 或 05 上看到此问题,则至少更新到 1.7.0_06 即可解决此问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-09
      • 1970-01-01
      • 1970-01-01
      • 2015-03-19
      • 2012-05-09
      相关资源
      最近更新 更多