Java特色就是自动垃圾回收,但是有时候垃圾回收并不是很理想,影响到我们自身服务对外提供支持了,此时,就需要根据GC的日志区看下回收的情况,以便调整出更好的效率。
这篇文章就来学习下怎么设置GC日志相关的一些参数以及怎么打印出来的日志分别是什么意思。
不同的垃圾收集器 会有不同的日志输出格式,我本地的环境是jdk1.8的,所以使用的垃圾收集器是默认的Parallel Scavenge+ Parallel old 本文就以这个收集器来测试的。
用参数 -Xms50m -Xmx50m -Xmn20m
来控制这块代码的的堆内存大小。
-Xms50m : 堆内存最小50M
-Xmx50m : 堆内存最大50M
-Xmn20m : 新生代20M,老年代就剩余30M
含义是 Java堆最大最小都是50MB 且不可变化,由于新生代可以分为Eden区和两个Survivor区(from区和to区),且比例是8:1:1,所以Eden区大概16M,from区和to区各2M。
PSYoungGen:表示垃圾收集器是Parallel Scavenge,回收的是新生代
ParOldGen:表示垃圾收集器是Parallel old,回收的是老年代
1. 最简单参数-XX:+PrintGC
代码如下:
/**
* vm参数:-Xms50m -Xmx50m -Xmn20m -XX:+PrintGC
*/
public class Allocation {
private static final int _1MB = 1024 * 1024;
private static void testAllocation(){
byte[] one = new byte[6*_1MB];
byte[] two = new byte[6*_1MB];
byte[] three = new byte[6*_1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
日志输出如下:
[GC (Allocation Failure) 15360K->13162K(48640K), 0.0092840 secs]
哎,就这么简短,日志的含义就是触发了一次minor gc,在GC前,堆空间的使用量是15360K(约16M),GC后的堆空间使用量为13162K(约12M),整个堆的空间是48640K。最后是本次GC花费的时间 0.0092840 secs 。
PrintGC打印的日志就是这么简短,只能看到总的信息,再具体的就看不到了。
2. 详细的参数:-XX:+PrintGCDetails
配置:-Xms50m -Xmx50m -Xmn20m -XX:+PrintGCDetails
日志输入如下(代码参考上面):
我们来逐句解释下上面日志的含义
- [GC (Allocation Failure) [PSYoungGen: 15105K->946K(17920K)] 15105K->13242K(48640K), 0.0118657 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
GC (Allocation Failure)表示发生的minor GC,新生代回收前使用量15105K,回收后使用量946K(总共17920K),整个堆占用从15105K下降到13242K(整堆的大小是48640K) 。最后是GC花费的时间,user表示用户态CPU耗时,sys表示系统CPU耗时,real表示GC的实际经历的时间 - PSYoungGen total 17920K, used 7397K
年轻代 总空间17920K,已使用了7397K (年轻代=eden+from/to) - eden space 15360K, 42% used
eden区空间15360K,已使用了42% - from space 2560K, 36% used
from区空间2560K,已使用了36% - to space 2560K, 0% used
to区空间2560K,已使用0% - ParOldGen total 30720K, used 12296K / object space 30720K, 40% used
老年代总空间30720K。已使用了12296K,百分比是40% - Metaspace used 3348K
元空间的使用,是不占用堆内存的
上面的是GC详细日志的参数输出内容,还有个更详细的参数,回收前回收后都打印出来,上面的日志如果看不出回收信息 还可以通过对比了解。
补充:当老年代内存不足的时候还会发生full GC,日志如下
0.211: [Full GC (Ergonomics) [PSYoungGen: 914K->0K(17920K)] [ParOldGen: 26632K->27351K(30720K)] 27546K->27351K(48640K), [Metaspace: 3350K->3350K(1056768K)], 0.0084485 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
3. 回收前后详细参数:-XX:+PrintHeapAtGC
-Xms50m -Xmx50m -Xmn20m -XX:+PrintHeapAtGC
日志输入如下(代码参考上面):
大部分都和上面日志内容是一样的,具体含义参考上面的,不过这次是打印了两个日志,
- Heap before GC invocations=1 (full 0):GC发生之前的堆空间的信息
- Heap after GC invocations=1 (full 0):GC发生之后堆空间的信息
4. GC发生的时间参数:-XX:PrintGCTimeStamps
-Xms50m -Xmx50m -Xmn20m -XX:+PrintGC -XX:+PrintGCTimeStamps
日志如下(代码参考上面)
0.199: [GC (Allocation Failure) 9268K->6970K(48640K), 0.0073891 secs]
上面中的【0.199】就是这次GC发生的时间 (这个时间是虚拟机启动后的偏移量)
5. 应用程序执行时间参数:-XX:+PrintGCApplicationConcurrentTime
-Xms50m -Xmx50m -Xmn20m -XX:+PrintGCTimeStamps -XX:+PrintGC -XX:+PrintGCApplicationConcurrentTime
日志如下(代码参考上面)
0.236: Application time: 0.0088226 seconds
0.236: [GC (Allocation Failure) 9268K->7018K(48640K), 0.0080782 secs]
0.251: Application time: 0.0074303 seconds
如上输出的是程序执行的时间
6. 应用程序停顿时间参数:-XX:+PrintGCApplicationStoppedTime
-Xms50m -Xmx50m -Xmn20m -XX:+PrintGCTimeStamps -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime
日志如下(代码参考上面)
0.181: [GC (Allocation Failure) 15360K->13210K(48640K), 0.0107874 secs]
0.192: Total time for which application threads were stopped: 0.0109662 seconds, Stopping threads took: 0.0000396 seconds
如上输出的是程序暂停的时间
7. 日志落盘参数:-Xloggc
-Xms50m -Xmx50m -Xmn20m -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/Users/ajisun/Desktop/logs/gc.log
默认情况下gc的日志会输出在控制台,但是不方便我们保存和分析定位问题,所以虚拟机还提供了日志输出文件的方式 ,用参数 -Xloggc来指定, 启动程序之后日志输出在对应目录下的gc.log文件中
8. 总结
文中总共介绍了7种GC日志相关的参数,我们可以根据这几种参数调整日志的输出。根据日志的内存观察系统的情况以及排查问题。
- 最简单参数-XX:+PrintGC
- 详细的参数:-XX:+PrintGCDetails
- 回收前后详细参数:-XX:+PrintHeapAtGC
- GC发生的时间参数:-XX:PrintGCTimeStamps
- 应用程序执行时间参数:-XX:+PrintGCApplicationConcurrentTime
- 应用程序停顿时间参数:-XX:+PrintGCApplicationStoppedTime
- 日志落盘参数:-Xloggc
我是纪先生,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏好文,欢迎关注公众号,做一个持续成长的技术人。
个人网站
1. 虚拟机系列:jvm运行时堆内存如何分代;
2. 虚拟机系列:jvm中的垃圾回收算法;
3. 虚拟机系列:jvm运行时数据区域;
4. 虚拟机系列:JVM中对象的创建,内存布局和访问定位;
5. 虚拟机系列:JVM中的垃圾收集器;
6. 虚拟机系列:JVM中的内存分配;