在上一篇文章中我分享了内存溢出的问题,也写了一个死循环 CUP 飙高的案例,今天我们来看看内存溢出该怎么排查。

jvisualvm

下面用 JDK 自带的工具 jvisualvm 来个小伙伴操作一遍,在命令行中搜索 jvisualvm 就会出来了。

java查看内存溢出模块 如何查看jvm内存溢出日志_4s

而 jvisualvm 的操作界面是这样的:

java查看内存溢出模块 如何查看jvm内存溢出日志_java查看内存溢出模块_02

下面我们还是用上一遍文章中的案例:

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 状况的插件。

java查看内存溢出模块 如何查看jvm内存溢出日志_java_03

按照步骤:

visualvm中找到 ”工具“选择 ”插件“ ,最后点击 ”可用插件“ 找到visual gc,因为我已经安装了找不到。

java查看内存溢出模块 如何查看jvm内存溢出日志_4s_04


之后点击”已安装“ 选择 visual gc 完成激活

java查看内存溢出模块 如何查看jvm内存溢出日志_java_05

java查看内存溢出模块 如何查看jvm内存溢出日志_Time_06

可以看到不释放,堆空间就一直上去,直到OOM(out of memory)

java查看内存溢出模块 如何查看jvm内存溢出日志_Time_07


这个时候我们点击 heap dump下来堆信息看看

java查看内存溢出模块 如何查看jvm内存溢出日志_Time_08

点击 “File” 选择 “Open heap Dump” ,默认会看到这个文件,选择就好

java查看内存溢出模块 如何查看jvm内存溢出日志_java_09


拉倒下面会看到这些信息

java查看内存溢出模块 如何查看jvm内存溢出日志_4s_10


我们再往下看看

java查看内存溢出模块 如何查看jvm内存溢出日志_4s_11


点击选择 java基础(java basics) 选择 “Thread Details”定位到具体的代码,发现问题

java查看内存溢出模块 如何查看jvm内存溢出日志_Time_12

看到了嘛,具体代码的位置都帮我们定位好了,那排查也就是手到擒来的事情了

最后来说一下 visual gc 从上往下对上图中的各个图标表及其状态进行说明:

Compile Time:编译情况

java查看内存溢出模块 如何查看jvm内存溢出日志_Time_13


789 compoles - 20.011s 表示编译总数为 789 ,编译总耗时为 20.011s。

一个脉冲表示一次JIT编译,脉冲越宽表示编译时间越长。Class Loader Time:类加载情况

java查看内存溢出模块 如何查看jvm内存溢出日志_4s_14


1592 loaded,0 unloaded - 14.245s 表示已加载的数量为 1592 ,卸载的数量为 0,耗时为 14.245s。

GC Time:总的(包含新生代和老年代)gc 情况记录

java查看内存溢出模块 如何查看jvm内存溢出日志_java查看内存溢出模块_15

64 collections,8.091s Last Cause:Allocation Failure表示一共经历了 64 次gc(包含Minor GC和Full GC),总共耗时,8.091s。

Eden Space:新生代 Eden 区内存使用情况

java查看内存溢出模块 如何查看jvm内存溢出日志_java查看内存溢出模块_16

(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区内存使用情况

java查看内存溢出模块 如何查看jvm内存溢出日志_java_17

(396.500M,396.5000M):61.21M 表示该 Survivor 区的最大容量为 396.500M(默认为Eden区的1/8),当前已用61.21M 。

Old Gen:老年代内存使用情况

java查看内存溢出模块 如何查看jvm内存溢出日志_4s_18

(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:方法区内存使用情况

java查看内存溢出模块 如何查看jvm内存溢出日志_java_19

(1.008G,8.875M):8.280M表示方法区最大容量为 1.008G,当前容量为 8.875M,当前使用量为8.280M。