我有一个在我的 Ubuntu 10.04 机器上运行的 Java 程序,无需任何用户交互,重复查询 MySQL 数据库,然后根据从数据库读取的数据构造 img 和 txt 文件。它会进行数以万计的查询并创建数以万计的文件。
运行几个小时后,我机器上的可用内存(包括交换空间)已完全用完。我没有启动其他程序,后台运行的进程不会消耗太多内存,也不会真正增加消耗。
为了找出分配这么多内存的原因,我想分析一个堆转储,所以我用 -Xms64m -Xmx128m -XX:+HeapDumpOnOutOfMemoryError 开始了这个过程。
令我惊讶的是,情况和以前一样,几个小时后,程序分配了所有的交换空间,远远超出了给定的 128m 的最大值。
用 VisualVM 调试的另一次运行显示堆分配永远不会超过最大值 128m - 当分配的内存接近最大值时,它的很大一部分会再次释放(我假设是垃圾收集器)。
因此,稳定增长的堆不会成为问题。
当内存全部用完时:
free 显示如下:
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 进程)
我的第一个问题是:
- 如何在操作系统级别(例如使用命令行工具)找出分配这么多内存的原因? top/htop 没有帮助我。如果有很多很多相同类型的微小进程占用内存:有没有办法智能地总结相似的进程? (我知道这可能是题外话,因为它是一个 Linux/Ubuntu 问题,但我的主要问题可能仍然与 Java 相关)
我的老问题是:
- 为什么顶部输出中没有给出我的程序的内存消耗?
- 如何找出分配这么多内存的原因?
- 如果堆不是问题,那么唯一的“分配因素”是堆栈吗? (这 堆栈应该不是问题,因为没有很深的“方法调用深度”)
- 作为数据库连接的外部资源呢?
最佳答案
如果您的 Java 进程确实是占用内存的进程,并且在 VisualVM 或内存转储中没有任何可疑之处,那么它必须位于 native 代码的某个位置 - 无论是在 JVM 中还是在您正在使用的某些库中。例如,在 JVM 级别上,如果您使用 NIO 或内存映射文件。如果您的某些库使用 native 调用,或者您的数据库使用的不是类型 4 的 JDBC 驱动程序,则可能存在泄漏。
一些建议:
关于java - Java程序的内存消耗问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9546449/