调优过程
准备工作
- 登录Java性能分析工具。
- 添加目标服务器(待分析的Java进程所在服务器)。图1 添加目标环境
- 启动在线分析功能。图2 启动在线分析
在线分析
- 概览页签查看进程整体情况。图3 概览
通过概览页面发现程序的堆使用情况在一段时间内不断升高,同时GC也比较频繁,但是堆却没有明显的被释放,可能存在内存泄露。
- GC页签查看GC详情。图4 GC页签
分析发现GC比较频繁的有YoungGC和OldGC,但是每次GC回收的内存可以忽略不计,甚至根本没有回收任何内存,此现象说明内存泄露的可能性很大。
- 内存页签查看内存详情。图5 内存页签
在内存转储页签下点击执行内存转储按钮生成堆转储文件,为了方便查看堆的变化可以保存快照,生成多次堆转储文件。
- 再次生成内存转储。图6 结果比较
通过生成两次堆转储文件发现TestMemoryLeak$Person这个类应该是自定义的类,内存占用情况比较多,并且随着时间呈上升趋势,需要重点观察。
可以利用快照功能提供的比较操作,方便查看两个堆转储文件中相同类实例与保留堆的变化情况,通过比较发现TestMemoryLeak$Person实例与保留堆是明显上升的趋势。
- 切换模式查看。图7 支配树视图
切换到支配树模式,发现TestMemoryLeak 类的保留堆占总堆的比例高达 99%,继续向下点开支配树发现大部分对象是 TestMemoryLeak$Person 持有的 byte[],到这里正好可以跟直方图中byte[] 占比较多可以对应的上。
- 结果分析。图8 代码片段
经过直方图与支配树的分析,TestMemoryLeak$Person类对应的实例有内存泄露嫌疑,根据支配树提供的内容应该是 HashSet 实例持有TestMemoryLeak$Person对象,接下来咱们可以搜索代码中的相关项看看。本案例中的demo 程序比较简单,直接搜索TestMemoryLeak类可以发现的确存在内存泄露点,一个线程不断的往hashSet和hashSetPerson两个静态对象添加 Person 实例,并且没有清除的逻辑,导致出现内存泄露。
采样分析
对目标程序进行采样分析,最好进行多次采样,采样时间相对长一点。
- 启动采样分析。图9 配置采样分析参数
- 查看采样分析结果。图10 采样分析结果
父主题: 实践1:内存泄漏调优实践