原文:Hunting Memory Leaks in Java
上一篇:捕获Java内存泄露 (二)
捕获Java内存泄露 (三)
并非OOM的应用程序崩溃
有时,应用程序可能会在本地堆的分配失败后迅速崩溃。如果你运行不检查由内存分配函数返回的错误的本地代码,则会发生这种情况。
例如,如果没有可用内存,malloc
系统调用返回NULL
。如果从malloc
返回结果没有被检查,那么当应用程序试图访问无效的内存地址时,它可能会崩溃。取决于环境的不同,这种类型的问题可能很难找到。
在某些情况下,从致命错误日志或崩溃时转储记录中给出的信息将是足够的。如果崩溃的原因被确定为在某些内存分配中缺少错误处理,那么你必须查找这次分配失败的原因.。与其他本地堆问题一样,系统可能只配置了不足的交换空间,或者另一个进程正在消耗所有可用的内存资源,等等。
诊断泄漏
在大多数情况下,诊断内存泄漏需要非常深入细节的关于问题应用程序的知识。警告:这个过程可能是冗长和反复的。
我们寻找内存泄漏的策略,将相对直接:
- 识别症状
- 启用
verbose
垃圾收集 - 启用分析器
- 分析踪迹
1. 识别症状
如上所述,在许多情况下,java的进程最终会抛出OOM运行时异常,明确的指出你的内存资源已经耗尽。在这种情况下,你需要区分出正常内存耗尽和内存泄漏。分析OOM的信息,基于上述讨论尽力找到它的罪魁祸首。
通常情况下,如果一个java应用程序请求的内存多于运行时堆提供的存储空间,它可以是由于糟糕的设计。例如,如果应用程序创建一个图像的多个副本或加载一个文件到数组中,而图像或文件是非常大的,它会耗尽内存。这是一个正常的资源枯竭。应用程序的工作符合设计(尽管这样的设计显然是愚蠢的)。
但是,如果应用程序在处理同一种数据时,其内存利用率稳步提高,你可能遇到一个内存泄漏。
2. 启用verbose
垃圾收集
最快的断定你确实有一个内存泄漏的方法是启用verbose
垃圾收集。内存约束的问题通常可以通过分析verbosegc
输出模式来识别。
具体而言,-verbosegc
参数允许你生成一个对每一次启动垃圾收集(GC)过程的跟踪。也就是说,当内存开始垃圾收集,汇总报告打印到标准错误输出,让你对你的内存是如何管理的有有一个了解。
下面是一些带–verbosegc
选项生成的典型的输出:
每一块(或节)在GC跟踪文件中由升序数字编号。为了让跟踪有意义,你应该看看连续分配失败的节和寻找是否有随着时间的推移,被释放的内存(字节数和百分比)在减少,而总的内存消耗(这里是19725304)在增加。这些都是内存耗尽的典型迹象。
3. 启用分析器
不同的jvm提供了不同的方式为生成跟踪文件来反映堆活动,但通常都包括关于对象类型和大小的详细信息。这被称之为heap profiling
(堆分析)。
4. 分析踪迹
这篇文章着重于Java VisualVM生成的踪迹。踪迹可以以不同的格式出现,因为它们可以由不同的工具生成,但它们深层的思路总是一致的:在堆中找到一块不应该存在的对象,并确定这些对象是否积累而不释放。特别感兴趣的是临时对象,众所周知,它在每一次java应用程序的某个事件触发时被分配。本应该只存在较少数量的对象实例存在多个,通常表示应用程序问题。
最后,解决内存泄漏需要你彻底审查你的代码。学习对象泄漏的类型会很有帮助,并大大提高调试速度。
下一篇:捕获Java内存泄露 (四)