我有一个在我的Ubuntu 10.04机器上运行的Java程序,在没有任何用户交互的情况下,反复查询MySQL数据库,然后根据从DB读取的数据构造img和txt文件。它可以生成数以万计的查询并创建数万个文件。
经过几个小时的运行后,我机器上的可用内存(包括交换空间)已经完全耗尽。我还没有启动其他程序,并且在后台运行的进程不会消耗太多内存,也不会真正增加消耗。
为了找出分配如此多内存的内容,我想分析一个堆转储,所以我用-Xms64m -Xmx128m -XX:+ HeapDumpOnOutOfMemoryError开始了这个过程。
令我惊讶的是,情况和以前一样,经过几个小时后,程序正在分配所有的交换,这超出了给定的最大值128米。
使用VisualVM调试的另一个运行显示堆分配永远不会超过最大值128m - 当分配的内存接近最大值时,其中很大一部分会再次释放(我假设是垃圾收集器)。
因此,稳定增长的堆不会成为一个问题。
当内存全部耗尽时:
免费显示以下内容:
total used free shared buffers cached
Mem: 2060180 2004860 55320 0 848 1042908
-/+ buffers/cache: 961104 1099076
Swap: 3227640 3227640 0
顶部显示以下内容:
USER VIRT RES SHR COMMAND
[my_id] 504m 171m 4520 java
[my_id] 371m 162m 4368 java
(到目前为止,两个“最大”进程和唯一运行的java进程)
我的第一个问题是:
我的老问题是:
答案 0 :(得分:7)
如果您的Java进程确实是占用内存的进程,并且VisualVM或内存转储中没有任何疑问,那么它必须位于本机代码中的某个位置 - 无论是在JVM中还是在您正在使用的某些库中。例如,在JVM级别上,如果您使用的是NIO或内存映射文件。如果你的某些库正在使用本机调用,或者你正在使用不为你的数据库键入4个JDBC驱动程序,那么可能存在泄漏。
一些建议:
答案 1 :(得分:2)
@ maximdim的回答是针对这种情况的一般建议。这里可能发生的是一个非常小的Java对象被保留,导致一些更大量的本机(OS级)内存闲置。 Java堆中不考虑此本机内存。 Java对象可能很小,以至于在Java对象保留将使堆压倒之前,您将很快达到系统内存限制。
所以找到这个的诀窍就是使用连续的堆转储,远远超过你已经注意到整个过程的内存增长,但不是那么远,以至于大量的工作已经开始。您正在寻找的是堆中的Java对象计数不断增加并且附加了本机内存。
这些可以是文件句柄,套接字,数据库连接或图像句柄,仅列举一些可能直接适用于您的文件句柄。
在更罕见的情况下,即使Java对象被垃圾回收,也会有java实现泄露的本机资源。我曾经遇到过一个WinCE 5漏洞,每个插座都关闭了4k漏洞。因此没有Java对象增长,但是有进程内存使用量增长。在这些情况下,制作一些计数器并跟踪具有本机内存的对象的Java分配与实际增长是有帮助的。然后在足够短的窗口上,您可以查找任何相关性并使用它们来制作更小的测试用例。
另外一个提示,请确保所有关闭操作都在finally块中,以防万一异常使您退出正常的控制流。众所周知,这也会引起这种问题。
答案 2 :(得分:1)
嗯......使用ipcs检查共享内存段是否未打开。检查JVM的打开文件描述符(/proc/<jvm proccess id>/fd/*
)。在顶部,键入fpFp
以显示交换并按使用交换任务列表排序。
这就是我现在能想出来的所有内容,希望它至少有一点帮助。
答案 3 :(得分:1)
正如@maximdim和@JamesBranigan指出的那样,可能的罪魁祸首是代码中的一些本机交互。但是,由于您无法准确追踪有问题的交互使用可用工具的位置,为什么不尝试使用强力方法呢?
您已经概述了一个两部分过程:查询MySQL和写入文件。这些事项中的任何一个都可以作为测试从流程中排除。测试一:消除查询并硬编码将返回的内容。测试二:执行查询,但不要费心写文件。你还有泄漏吗?
可能还有其他可测试案例,具体取决于您的应用程序的其他用途。
答案 4 :(得分:1)
您是否正在创建单独的线程来运行“任务”?用于创建线程的内存与Java堆是分开的。
这意味着即使您指定-Xmx128m
,Java进程使用的内存可能会高得多,具体取决于您使用的线程数和线程堆栈大小(每个线程获得一个分配的堆栈,由-Xss
)指定的大小。
最近工作的例子:
我们有一个4GB的Java堆(-Xmx4G
),但操作系统进程耗费超过6GB,
也用完了交换空间。
当我用cat /proc/<PID>/status
检查进程状态时,我注意到我们运行了11000个线程。
由于我们设置了-Xss256K
,因此很容易解释:10000个线程意味着2.5GB。
答案 5 :(得分:0)
您的文件系统缓存可能导致此问题,文件系统缓存会在执行大量IO时占用所有可用内存。系统性能不应受此行为的不利影响,内核将在进程请求内存时立即释放文件系统缓存。
答案 6 :(得分:0)
由于当天没有活动我问了问题(直到3月23日),而且由于我仍然无法找到记忆消耗的原因,我“务实地”解决了这个问题。
导致问题的程序基本上是“任务”的重复(即查询数据库然后创建文件)。参数化程序相对容易,因此执行某个任务子集而不是所有任务。
所以现在我反复从shell脚本运行我的程序,在每个进程中只执行一组任务(通过参数参数化)。最后,所有任务都在执行,但由于单个进程只处理任务子集,因此不再存在内存问题。
对我来说这是一个充分的解决方案。如果您遇到类似的问题并且您的程序具有类似批处理的执行结构,那么这可能是一种实用的方法。
当我找到时间时,我会调查新的建议,希望找出根本原因(感谢您的帮助!)。
答案 7 :(得分:0)
你说你在创建图像文件是在创建图像对象吗?如果是这样,你完成后对这些对象调用dispose()吗?
如果我没记错的话,java awt想象对象会分配必须明确处理的本机资源。