垃圾回收实战
接下来根据详细的栗子,说明GC日志的查看,JVM参数的调整。
JVM参数配置
工作中常用的JVM参数配置,及如何调优。
JVM参数类型
- 标准参数(-),JVM各个版本基本保持不变,相对稳定且向后兼容;
- 非标准参数(-X),变化较小的参数,默认JVM实现参数,且不保证向后兼容;
- 非Stable参数(-XX),各个JVM的实现不同,将来可能会取消。
标准参数
标准参数可以输入java
查看:
1 | 用法: java [-options] class [args...] |
非标准参数
非标准参数可以输入:java -X
查看:
1 | -Xmixed 混合模式执行(默认) |
非stable参数
使用-XX:+PrintFlagsInitial
查看初始属性。
主要分为键值对、布尔型参数。
键值对:-XX:属性key=属性value
,比如,-XX:MetaspaceSize=128m
表示设置元空间的大小。
布尔型:-XX:+属性值(开启)/-属性值(关闭)
,比如,-XX:+PrintGCDetails
打印GC的细节。
常用参数配置
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-Xms |
初始堆大小 | 内存的1/64 |
默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制. |
-Xmx |
最大堆大小 | 内存的1/4 |
默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制. |
-Xmn |
年轻代的大小 | 整个堆的3/8 |
|
-Xss |
每个线程堆栈的大小 | 一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k | |
-XX:NewSize |
年轻代的大小 | ||
-XX:NewRatio |
年轻代比例 | 配置年老代和年轻代的比例,如2,则年老与年轻比为:2:1 |
|
-XX:SurvivorRatio |
Eden区和Survivor区的大小比值 | 8 | 默认为8,则Eden和两个Survivor的比值为8:1:1 |
-XX:PretenureSizeThreshold |
对象超过多大时直接再老年代分配 | 单位为字节 | |
-XX:MaxTenuringThreshold |
垃圾最大的年龄 | 15,Java8 | 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行GC时才有效. |
-XX:+HeapDumpOnOutOfMemoryError |
堆栈溢出时导出dump文件 | 可以使用MAT分析 |
注:SurvivorRatio
的计算公式如下:
R为SurvivorRatio的值,Y为新生代的值,则:SurvivorRatio
是R,则比例就是R:1:1
。
并行收集器相关参数:
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-XX:+UseParNewGC |
设置年轻代为并行收集 | 可与CMS收集同时使用 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值 | |
-XX:+UseParallelOldGC |
设置老年代为并行收集器 | ||
-XX:ParallelGCThreads |
并行收集器的线程数 | 此值最好配置与处理器数目相等 同样适用于CMS | |
-XX:MaxGCPauseMillis |
每次年轻代垃圾回收的最长时间(最大暂停时间) | 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值. | |
-XX:+UseAdaptiveSizePolicy |
自动选择年轻代区大小和相应的Survivor区比例 | 设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开. | |
-XX:GCTimeRatio |
设置垃圾回收时间占程序运行时间的百分比 |
CMS相关参数
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-XX:+UseConcMarkSweepGC |
使用CMS收集器 | ||
-XX:CMSFullGCsBeforeCompaction |
多少次后压缩存储 | 运行多少次GC后,整理碎片。 | |
-XX:+CMSParallelRemarkEnabled |
降低标记停顿 | ||
-XX+UseCMSCompactAtFullCollection |
在FULL GC的时候, 对年老代的压缩 | CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片 | |
-XX:CMSInitiatingOccupancyFraction |
使用cms作为垃圾回收 使用百分之多少后开始CMS收集 | 92 | 因为CMS并发清除垃圾,会导致一边生成一边清理,因此不能占满后才开始清除,可以设置70,当出现promotion failed时,还要根据需要减小设置。 |
辅助信息
参数 | 含义 | 说明 |
---|---|---|
-XX:+PrintGC |
输出GC日志 | 回收前后内存及耗时 |
-XX:+PrintGCDetails |
输出较详细的GC日志 | 包括GC的收集器名称 |
-XX:+PrintGCTimeStamps |
输出耗时时间戳 | |
-XX:+PrintGCApplicationStoppedTime |
打印垃圾回收期间程序暂停的时间.可与上面混合使用 | |
-XX:+PrintHeapAtGC |
打印GC前后的详细堆栈信息 | |
-Xloggc:filename |
把相关日志信息记录到文件以便分析. 与上面几个配合使用 | |
-XX:+PrintTLAB |
查看TLAB空间的使用情况 |
工具的使用
MAT分析堆转储文件
线上程序运行的时候,可以加上-XX:+HeapDumpOnOutOfMemoryError
参数来导出内存溢出时的堆信息,生成 hrof 文件, 添加-XX:HeapDumpPath
可以指定 hprof 文件的生成路径,如果不指定则 hrof 文件生成在与字节码文件相同的目录下。
下载MemoryAnalyzer
将生成的hprof文件导入后,可以根据报告,找到内存溢出的错误发生的位置。
VisualVM
jdk自带的分析工具,可以连接到本地运行的java程序,通过丰富的插件来观察运行过程中的JVM使用情况。
jconsole
也是JDK自带的工具,可以远程连接java程序查看运行状况。
GC日志的分析
日志如下
1 | 10.703: [GC (Allocation Failure) 10.703: [DefNew: 8192K->1024K(9216K), 0.0105346 secs] 8192K->3547K(19456K), 0.0106322 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] |
开头的时间表示自虚拟机启动后的时间。
GC和Full GC表示垃圾收集的类型,Full GC会有STW,有一定的停顿时间;
Allocation Failure表示触发GC的原因;
DefNew、Tenured、Metaspace表示发生GC的区域,与用到的收集器有关,如Serial收集器就为DefNew,如果是ParNew收集器则名称为ParNew;
** 8192K->1024K(9216K)表示:GC 前该内存区域已使用容量 -> GC 后该内存区域已使用容量(该内存区域总容量);
**0.0105346 secs表示整个GC所用的时间,如果多个区域GC则表示是每个区域的耗时;
Times: user=0.02 sys=0.00, real=0.02 secs代表用户态消耗的 CPU 时间,内核态消耗的 CPU 时间,和操作从开始到结束所经过的墙钟时间,墙钟时间包括各种非运算的等待耗时,例如等待磁盘 I/O,等待线程阻塞,而 CPU 时间不包括这些耗时,但当系统有多 CPU 或者多核的话,多线程操作会叠加这些 CPU 时间,所以 user 或 sys 时间是可能超过 real 时间的。