我已经查看过关于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的做法有何不同?
答案 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将解决这个问题。