在上一篇文章中我分享了内存溢出的问题,也写了一个死循环 CUP 飙高的案例,今天我们来看看内存溢出该怎么排查。
jvisualvm
下面用 JDK 自带的工具 jvisualvm 来个小伙伴操作一遍,在命令行中搜索 jvisualvm 就会出来了。
而 jvisualvm 的操作界面是这样的:
下面我们还是用上一遍文章中的案例:
public static void main(String[] args) {
Map map = System.getProperties();
Random r = new Random();
while (true) {
map.put(r.nextInt(), "value");
}
}
visual gc 是 visualvm 中的图形化查看 GC 状况的插件。
按照步骤:
visualvm中找到 ”工具“选择 ”插件“ ,最后点击 ”可用插件“ 找到visual gc,因为我已经安装了找不到。
之后点击”已安装“ 选择 visual gc 完成激活
可以看到不释放,堆空间就一直上去,直到OOM(out of memory)
这个时候我们点击 heap dump下来堆信息看看
点击 “File” 选择 “Open heap Dump” ,默认会看到这个文件,选择就好
拉倒下面会看到这些信息
我们再往下看看
点击选择 java基础(java basics) 选择 “Thread Details”定位到具体的代码,发现问题
看到了嘛,具体代码的位置都帮我们定位好了,那排查也就是手到擒来的事情了
最后来说一下 visual gc 从上往下对上图中的各个图标表及其状态进行说明:
Compile Time:编译情况
789 compoles - 20.011s 表示编译总数为 789 ,编译总耗时为 20.011s。
一个脉冲表示一次JIT编译,脉冲越宽表示编译时间越长。Class Loader Time:类加载情况
1592 loaded,0 unloaded - 14.245s 表示已加载的数量为 1592 ,卸载的数量为 0,耗时为 14.245s。
GC Time:总的(包含新生代和老年代)gc 情况记录
64 collections,8.091s Last Cause:Allocation Failure表示一共经历了 64 次gc(包含Minor GC和Full GC),总共耗时,8.091s。
Eden Space:新生代 Eden 区内存使用情况
(1.162G,176.000M): 176.000M,17 collections,11.997s 表示 Eden 区的最大容量为 1.162G,当前容量为176.000M,当前已使用 176.000M,从开始监控到现在在该内存区域一共发生了 17 次gc(Minor GC),gc总耗时为11.997s。
Survivor 0和Survivor 1:新生代的两个Survivor区内存使用情况
(396.500M,396.5000M):61.21M 表示该 Survivor 区的最大容量为 396.500M(默认为Eden区的1/8),当前已用61.21M 。
Old Gen:老年代内存使用情况
(2.326G,2.326G):2.325G, 47 collections,56.094s 表示老年区的最大容量为 2.326G,当前容量为2.326G,当前已用 2.325G,从开始监控到现在在该内存区域一共发生了 47 次gc(Full GC),gc总耗时为 56.094s,换算下可以看出单次 Full GC 要比 Minor GC 耗时长很多。
Metaspace:方法区内存使用情况
(1.008G,8.875M):8.280M表示方法区最大容量为 1.008G,当前容量为 8.875M,当前使用量为8.280M。