前言
学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。
既然如此,那么在IDE的控制台打印GC日志是必不可少的了。现在就告诉你怎么打印。
Eclipse配置打印GC日志
在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:
IntelliJ IDEA 配置打印GC日志
在上图的箭头处加上-XX:+PrintGCDetails这句话。于是,运行程序后,GC日志就可以打印出来了:
当然了,光有-XX:+PrintGCDetails
这一句参数肯定是不够的,下面我们详细介绍一下更多的参数配置。
一、Trace跟踪参数:
- 打印GC的简要信息:解释:可以打印GC的简要信息。比如:
1
2-verbose:gc
-XX:+printGC
[GC 4790K->374K(15872K), 0.0001606 secs]
[GC 4790K->374K(15872K), 0.0001474 secs]
[GC 4790K->374K(15872K), 0.0001563 secs]
[GC 4790K->374K(15872K), 0.0001682 secs]
上方日志的意思是说,GC之前,用了4M左右的内存,GC之后,用了374K内存,一共回收了将近4M。内存大小一共是16M左右。
2. 打印GC的详细信息:
1 | -XX:+PrintGCDetails |
解释:打印GC详细信息。
1 | -XX:+PrintGCTimeStamps |
解释:打印CG发生的时间戳。
理解GC日志的含义:
例如下面这段日志:
[GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
上方日志的意思是说:这是一个新生代的GC。方括号内部的“4416K->0K(4928K)”含义是:“GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量)”。而在方括号之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)”。
再往后看,“0.0001897 secs”表示该内存区域GC所占用的时间,单位是秒。
再比如下面这段GC日志:
上图中,我们先看一下用红框标注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含义,它表示新生代在内存当中的位置:第一个参数是申请到的起始位置,第二个参数是申请到的终点位置,第三个参数表示最多能申请到的位置。上图中的例子表示新生代申请到了15M的控件,而这个15M是等于:(eden space的12288K)+(from space的1536K)+(to space的1536K)。
疑问:分配到的新生代有15M,但是可用的只有13824K,为什么会有这个差异呢?等我们在后面的文章中学习到了GC算法之后就明白了。
- 指定GC log的位置:解释:指定GC log的位置,以文件输出。帮助开发人员分析问题。
1
-Xloggc:log/gc.log
1 | -XX:+PrintHeapAtGC |
解释:每一次GC前和GC后,都打印堆信息。
上图中,红框部分正好是一次GC,红框部分的前面是GC之前的日志,红框部分的后面是GC之后的日志。
1 | -XX:+TraceClassLoading |
解释:监控类的加载。
例如:
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.CharSequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.lang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
1 | -XX:+PrintClassHistogram |
解释:按下Ctrl+Break后,打印类的信息。
例如:
二、堆的分配参数:
-Xmx –Xms:指定最大堆和最小堆
举例、当参数设置为如下时:1
-Xmx20m -Xms5m
然后我们在程序中运行如下代码:
1
2
3
4
5
6
7
8// 系统的最大空间
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");
// 系统的空闲空间
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M");
// 当前可用的总空间
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");运行效果:
保持参数不变,在程序中运行如下代码:(分配1M空间给数组)
1
2
3
4byte[] b = new byte[1024 * 1024];
System.out.println("分配了1M空间给数组");
运行效果:
注:Java会尽可能将total mem的值维持在最小堆。
保持参数不变,在程序中运行如下代码:(分配10M空间给数组)
1
2byte[] b = new byte[10 * 1024 * 1024];
System.out.println("分配了10M空间给数组");运行效果:
保持参数不变,在程序中运行如下代码:(进行一次GC的回收)
1
System.gc();