我正在开发一个庞大的遗留Java应用程序,有很多手写的东西,现在你可以让框架处理。
我现在面临的问题是我们的Solaris服务器上的文件句柄不足。我想知道跟踪打开文件句柄的最佳方法是什么?在哪里查看以及什么可能导致打开文件句柄用完?
我无法在Solaris下调试应用程序,只能在我的Windows开发环境中调试。分析Windows下的打开文件句柄是否合理?
答案 0 :(得分:8)
我发现用于跟踪未关闭的文件句柄的一件好事是FindBugs:
http://findbugs.sourceforge.net/
它会检查很多东西,但其中最有用的是资源打开/关闭操作。它是一个静态分析程序,可以在您的源代码上运行,也可以作为eclipse插件使用。
答案 1 :(得分:7)
在Windows上,您可以使用进程资源管理器查看打开的文件句柄:
http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx
在Solaris上,您可以使用“lsof”来监视打开的文件句柄
答案 2 :(得分:3)
值得注意的是,开放套接字也会占用Unix系统上的文件句柄。所以它很可能就像数据库连接池泄漏(例如打开的数据库连接未被关闭并返回到池中)导致了这个问题 - 当然我在连接池泄漏之前看到过这个错误。
答案 3 :(得分:2)
回答问题的第二部分:
什么会导致打开文件句柄用完?
显然打开了很多文件,然后没有关闭它们。
最简单的情况是,在关闭之前,对包含本机句柄的任何对象的引用(例如,FileInputStream
)将被丢弃,这意味着文件在对象完成之前保持打开状态。
另一个选项是对象存储在某个地方而不是关闭。堆转储可能能够告诉您在JDK中包含哪些内容(jmap
和jhat
,或者如果需要GUI,则可以使用jvisualvm
。您可能对寻找拥有FileDescriptor
s。
答案 4 :(得分:2)
当我需要测试ic计数时,这个小脚本帮助我关注打开文件的数量。 如果在Linux上使用,那么对于Solaris你应该修补它(可能是:))
#!/bin/bash
COUNTER=0
HOW_MANY=0
MAX=0
# do not take care about COUNTER - just flag, shown should we continie or not
while [ $COUNTER -lt 10 ]; do
#run until process with passed pid alive
if [ -r "/proc/$1" ]; then
# count, how many files we have
HOW_MANY=`/usr/sbin/lsof -p $1 | wc -l`
#output for live monitoring
echo `date +%H:%M:%S` $HOW_MANY
# uncomment, if you want to save statistics
#/usr/sbin/lsof -p $1 > ~/autocount/config_lsof_`echo $HOW_MANY`_`date +%H_%M_%S`.txt
# look for max value
if [ $MAX -lt $HOW_MANY ]; then
let MAX=$HOW_MANY
echo new max is $MAX
fi
# test every second. if you don`t need so frequenlty test - increase this value
sleep 1
else
echo max count is $MAX
echo Process was finished
let COUNTER=11
fi
done
此外,您可以尝试使用jvm ontion -Xverify:none - 它应该禁用jar验证(如果大多数打开的文件是jar ...)。 对于通过未关闭的FileOutputStream泄漏,您可以使用findbug(上面提到过)或尝试查找文章如何修补标准java FileOutputStream / FileInputStream,您可以在其中查看,谁打开文件,并忘记关闭它们。不幸的是,现在找不到这篇文章,但这是现有的:) 还要考虑增加filelimit - 对于最新的* nix内核来说,处理超过1024 fd的问题不是一个问题。
答案 5 :(得分:2)
这在您的情况下可能不实用,但是当我遇到与开放式数据库连接类似的问题时,我所做的就是用我自己的方式覆盖“打开”功能。 (方便的是我已经有了这个功能,因为我们编写了自己的连接池。)在我的函数中,我在一个记录open的表中添加了一个条目。我做了一个堆栈跟踪调用,并保存了调用者的标识,以及调用的时间,我忘了还有什么。释放连接后,我删除了表条目。然后我有一个屏幕,我们可以转储打开的条目列表。然后,您可以查看时间戳,轻松查看哪些连接在不太可能的时间内打开,以及哪些功能已打开。
由此我们能够快速找到打开连接并且无法关闭它们的几个功能。
如果你有很多打开的文件句柄,那么当你在某个地方完成时,你可能无法关闭它们。你说你已经检查了正确的try / finally块,但是我怀疑代码中的某个地方你错过了一个坏的,或者你有一个功能,并且从未实现最终。我想你每次打开文件时都可能正确地关闭,但是你同时打开了数百个文件。如果是这种情况,我不确定你能做什么,除了重新设计一个严肃的程序来操作更少的文件,或重新设计一个严肃的程序来排队你的文件访问。 (此时我添加了通常的“不知道你的申请细节等等。”
答案 6 :(得分:1)
我首先要求我的系统管理员获取该进程的所有打开文件描述符的列表。不同的系统以不同的方式执行此操作:例如,Linux具有/proc/PID/fd
目录。我记得Solaris有一个命令(也许是 pfiles ?)会做同样的事情 - 你的系统管理员应该知道它。
但是,除非你看到很多对同一文件的引用,否则fd列表对你没有帮助。如果它是一个服务器进程,它可能有很多文件(和套接字)打开了一个原因。解决问题的唯一方法是调整打开文件的系统限制 - 您还可以使用 ulimit 检查每用户限制,但在大多数当前安装中等于系统限制。
答案 7 :(得分:1)
不能直接回答您的问题,但这些问题可能是您的旧代码中错误地释放文件资源的结果。例如,如果您正在使用FileOutputsStream类,请确保在finally块中调用close方法,如下例所示:
FileOutputsStream out = null;
try {
//You're file handling code
} catch (IOException e) {
//Handle
} finally {
if (out != null) {
try { out.close(): } catch (IOException e) { }
}
}
答案 8 :(得分:1)
我会仔细检查Solaris盒子上的环境设置。我相信默认情况下,Solaris每个进程只允许256个文件句柄。对于服务器应用程序,特别是如果它在专用服务器上运行,这非常低。图50或更多用于打开JRE和库JAR的描述符,然后至少每个传入请求和数据库查询的一个描述符,可能更多,你可以看到这样做不会削减芥末一个严肃的服务器。
查看/etc/system
文件,了解rlim_fd_cur
和rlim_fd_max
的值,了解您的系统设置了什么。然后考虑这是否合理(您可以看到在服务器使用lsof
命令运行时打开了多少文件描述符,理想情况下使用-p [进程ID]参数。
答案 9 :(得分:0)
它当然可以给你一个想法。由于它是Java,因此应该类似地实现文件打开/关闭机制(除非其中一个JVM实现不正确)。我建议在Windows上使用File Monitor。
答案 10 :(得分:0)
谷歌为系统内部的一个名为filemon的应用程序。
顺便说一句,要跟踪此情况,您可以使用类似aspectj的内容来记录打开和关闭文件的所有调用,并记录它们发生的位置。答案 11 :(得分:0)
这是一种有助于查找未封闭资源的编码模式。它会关闭资源并在日志中抱怨问题。
class
{
boolean closed = false;
File file;
close() {
closed = true;
file.close();
}
finalize() {
if (!closed) {
log error "OI! YOU FORGOT TO CLOSE A FILE!"
file.close();
}
}
将上述file.close()调用包含在忽略错误的try-catch块中。
此外,Java 7还有一个新的尝试资源'可以自动关闭资源的功能。