要查看gc日志,那么首先得把gc日志进行输出,在JVM启动的时候添加参数:
-XX:+PrintGCDetails 打印GC日志细节
-XX:+PrintGCTimeStamps 打印GC日志时间
-Xloggc:gc.log 将GC日志输出到指定的磁盘文件上去,这里会把gc.log输出到项目根路径
然后JVM在运行过程中如果发生gc,那么将会把gc日志输出到gc.log中。
现在我们手动模拟一下Young GC的发生,然后查看gc日志。
配置JVM参数:
-XX:NewSize=5242880
-XX:MaxNewSize=5242880
-XX:InitialHeapSize=10485760
-XX:MaxHeapSize=10485760
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=15
-XX:PretenureSizeThreshold=10485760
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:gc.log
这里配置了新生代大小为5M,堆内存大小为10M,那么老年代大小就是5M。SurvivorRatio=8,那么Eden区就是Survivor区的8倍,所以Eden区占4M,两个Survivor都是占0.5M。MaxTenuringThreshold=15,那么新生代对象只有达到15岁才会进入老年代。PretenuringSizeThreshold等于10M,那么只有大于10M的对象才能直接在老年代分配。使用ParNew + CMS垃圾回收器。
写一个简单的main方法,来运行:
public static void main(String[] args) {
byte[] array = new byte[1024 * 1024];
array = new byte[1024 * 1024];
array = new byte[1024 * 1024];
array = null;
//触发Young GC
byte[] array1 = new byte[2 * 1024 * 1024];
}
在上面的代码中,我们可以推测一下GC的具体发生过程。
如果是在IDEA中,那么配置JVM参数可以点开Edit Configurations:
勾选 Add VM options,然后在VM options的框中输入对应的JVM启动参数,然后运行main方法。
在项目的根路径下,就可以看到一个gc.log,点进去查看:
CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=5242880 -XX:NewSize=5242880 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
0.383: [GC (Allocation Failure) 0.384: [ParNew: 3543K->512K(4608K), 0.0024598 secs] 3543K->1828K(9728K), 0.0026607 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.386: [GC (Allocation Failure) 0.387: [ParNew: 2627K->0K(4608K), 0.0015255 secs] 3943K->1828K(9728K), 0.0015985 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 4608K, used 2089K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 51% used [0x00000000ff600000, 0x00000000ff80a558, 0x00000000ffa00000)
from space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
to space 512K, 0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
concurrent mark-sweep generation total 5120K, used 1828K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3101K, capacity 4620K, committed 4864K, reserved 1056768K
class space used 327K, capacity 392K, committed 512K, reserved 1048576K
第一段内容中输出了JVM启动参数,里面都是按照我们配置的参数来的,还有一些别的默认参数。
第二段内容中可以看到进行了两次GC。
0.383: [GC (Allocation Failure) 0.384: [ParNew: 3543K->512K(4608K), 0.0024598 secs] 3543K->1828K(9728K), 0.0026607 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.386: [GC (Allocation Failure) 0.387: [ParNew: 2627K->0K(4608K), 0.0015255 secs] 3943K->1828K(9728K), 0.0015985 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
JVM运行的第0.383秒,发生Allocation Failure,就是给对象分配内存的时候内存空间不够了,所以在第0.384秒的时候,使用ParNew垃圾回收器进行了Young GC。
[ParNew: 3543K->512K(4608K),0.0024598 secs] : 新生代的总内存大小为4608KB(4M的Eden区 加上了 0.5M的 Survivor From区,一共是4.5M,即4608M);Young GC之前,新生代占用内存大小为3543KB(三个1M的数组,加上471KB的“未知内容”);Young GC之后,剩余了512KB(“未知内容”)。
3543K->1828K(9728K) : 整个堆内存大小为9728KB(即4M的Eden区加上0.5M的Survivor区加上5M的老年代大小,9.5M,即9728KB)。在Young GC之前,堆内存共有3543KB内存被占用(三个1M的数组,加上471KB的“未知内容”);在Young GC之后,堆内存共有1828KB。
第三段内容输出了Young GC之后的堆内存使用快照:
Heap
par new generation total 4608K, used 2089K [0x00000000ff600000, 0x00000000ffb00000, 0x00000000ffb00000)
eden space 4096K, 51% used [0x00000000ff600000, 0x00000000ff80a558, 0x00000000ffa00000)
from space 512K, 0% used [0x00000000ffa00000, 0x00000000ffa00000, 0x00000000ffa80000)
to space 512K, 0% used [0x00000000ffa80000, 0x00000000ffa80000, 0x00000000ffb00000)
concurrent mark-sweep generation total 5120K, used 1828K [0x00000000ffb00000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3101K, capacity 4620K, committed 4864K, reserved 1056768K
class space used 327K, capacity 392K, committed 512K, reserved 1048576K
可以看到新生代占用了2098KB(一个2M的数组,还有一点“未知内容”);Eden区被占用了51%,Survivor From和Survivor To使用率为0%,CMS管理的老年代被使用了1828KB。
1、JVM是如何工作的?_jerry_dyy的博客-CSDN博客_jvm是如何运行的
2、JVM的类加载机制_jerry_dyy的博客-CSDN博客
3、JVM内存区域划分_jerry_dyy的博客-CSDN博客_jvm的内存区域划分
4、JVM垃圾回收机制_jerry_dyy的博客-CSDN博客
5、JVM分代模型--新生代 的垃圾回收_jerry_dyy的博客-CSDN博客_jvm新生代划分
6、JVM分代模型--老年代 的垃圾回收_jerry_dyy的博客-CSDN博客
7、常见的垃圾回收器_jerry_dyy的博客-CSDN博客
9、学会查看GC日志_jerry_dyy的博客-CSDN博客
10、摸清JVM运行状况_jerry_dyy的博客-CSDN博客