1:在 IDE 的后台打印 GC 日志
Eclipse 设置
箭头处加上-XX:+PrintGCDetails这句话
IDEA 设置
5.1:Trace 跟踪打印
1、打印 GC 的简要信息:
-verbose:gc
-XX:+printGC
解释:可以打印 GC 的简要信息。比如:
[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 的详细信息:
-XX:+PrintGCDetails
解释:打印 GC 详细信息。
-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 算法之后就明白了。
3、指定 GC log 的位置:
-Xloggc:log/gc.log
解释:指定 GC log 的位置,以文件输出。帮助开发人员分析问题。
[
-XX:+PrintHeapAtGC
解释:每一次 GC 前和 GC 后,都打印堆信息。
例如:
上图中,红框部分正好是一次 GC,红框部分的前面是 GC 之前的日志,红框部分的后面是 GC 之后的日志。
-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]
-XX:+PrintClassHistogram
解释:按下 Ctrl+Break 后,打印类的信息。
例如:
5.2:堆的分配参数
1、-Xmx –Xms,-Xss:指定最大堆和最小堆,指定栈空间
2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:
- -Xmn设置新生代大小
- -XX:NewRatio
新生代(eden+2*s)和老年代(不包含永久区)的比值
例如:4,表示新生代:老年代=1:4,即新生代占整个堆的 1/5
- -XX:SurvivorRatio(幸存代)
设置两个 Survivor 区和 eden 的比值
例如:8,表示两个 Survivor:eden=2:8,即一个 Survivor 占年轻代的 1/10
3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath
- -XX:+HeapDumpOnOutOfMemoryError
OOM 时导出堆到文件
根据这个文件,我们可以看到系统 dump 时发生了什么。
- -XX:+HeapDumpPath
导出 OOM 的路径
导出的文件使用专门的工具进行打开,可参考第六章
4、-XX:OnOutOfMemoryError:
- -XX:OnOutOfMemoryError
在 OOM 时,执行一个脚本。
可以在 OOM 时,发送邮件,甚至是重启程序。
例如我们设置如下的参数:
-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是当前进程的pid
上方参数的意思是说,执行 printstack.bat 脚本,而这个脚本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即当程序 OOM 时,在 D:/a.txt 中将会生成线程的 dump。
目前生产上
-Xms8192M
-Xmx8192M
-XX:NewRatio=1 //设置新生代和老年代的比例为1:1
-XX:SurvivorRatio=6 //设置幸存代与Eden区的比例为2:6
-XX:MetaspaceSize=256M //元空间大小
-XX:MaxMetaspaceSize=512M
采用 ZGC 生产上的参数设置:
-Xms10G -Xmx10G
-XX:ReservedCodeCacheSize=256m -XX:InitialCodeCacheSize=256m
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
-XX:ConcGCThreads=2 -XX:ParallelGCThreads=6
-XX:ZCollectionInterval=120 -XX:ZAllocationSpikeTolerance=5
-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive
-Xlog:safepoint,classhisto=trace,age,gc=info:file=/opt/logs/logs/gc-%t.log:time,tid,tags:filecount=5,filesize=50m
-Xms -Xmx:堆的最大内存和最小内存,这里都设置为 10G,程序的堆内存将保持 10G 不变。
-XX:ReservedCodeCacheSize -XX:InitialCodeCacheSize: 设置 CodeCache 的大小, JIT 编译的代码都放在 CodeCache 中,一般服务 64m 或 128m 就已经足够。我们的服务因为有一定特殊性,所以设置的较大,后面会详细介绍。
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC:启用 ZGC 的配置。
-XX:ConcGCThreads:并发回收垃圾的线程。默认是总核数的 12.5%,8 核 CPU 默认是 1。调大后 GC 变快,但会占用程序运行时的 CPU 资源,吞吐会受到影响。
-XX:ParallelGCThreads:STW 阶段使用线程数,默认是总核数的 60%。
-XX:ZCollectionInterval:ZGC 发生的最小时间间隔,单位秒。
-XX:ZAllocationSpikeTolerance:ZGC 触发自适应算法的修正系数,默认 2,数值越大,越早的触发 ZGC。
-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive:是否启用主动回收,默认开启,这里的配置表示关闭。
*-Xlog:设置 GC 日志中的内容、格式、位置以及每个日志的大小。
5、堆的分配参数总结:
- 根据实际事情调整新生代和幸存代的大小,默认为新生代占堆内存 1/3,老年代占 2/3
- 官方推荐新生代占堆的 3/8
- 幸存代占新生代的 1/10
-
6、永久区分配参数:
-XX:PermSize -XX:MaxPermSize
设置永久区的初始空间和最大空间。也就是说,jvm 启动时,永久区一开始就占用了 PermSize 大小的空间,如果空间还不够,可以继续扩展,但是不能超过 MaxPermSize,否则会 OOM。
他们表示,一个系统可以容纳多少个类型
5.3:栈的分配参数
1、Xss:
设置栈空间的大小。通常只有几百 K
决定了函数调用的深度
每个线程都有独立的栈空间
局部变量、参数 分配在栈上
注:栈空间是每个线程私有的区域。栈里面的主要内容是栈帧,而栈帧存放的是局部变量表,局部变量表的内容是:局部变量、参数。
我们来看下面这段代码:(没有出口的递归调用)
public class TestStackDeep {
private static int count = 0;
public static void recursion(long a, long b, long c) {
long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10;
count++;
recursion(a, b, c);
}
public static void main(String args[]) {
try {
recursion(0L, 0L, 0L);
} catch (Throwable e) {
System.out.println(“deep of calling = “ + count);
e.printStackTrace();
}
}
}
上方这段代码是没有出口的递归调用,肯定会出现 OOM 的。
如果设置栈大小为 128k:
-Xss128K
运行效果如下:(方法被调用了 294 次)
如果设置栈大小为 256k:(方法被调用 748 次)
GC 常用参数
- -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
- -XX:+UseTLAB 使用 TLAB,默认打开
- -XX:+PrintTLAB 打印 TLAB 的使用情况
- -XX:TLABSize 设置 TLAB 大小
- -XX:+DisableExplictGC System.gc()不管用 ,FGC
- -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintHeapAtGC
- -XX:+PrintGCTimeStamps
- -XX:+PrintGCApplicationConcurrentTime (低) 打印应用程序时间
- -XX:+PrintGCApplicationStoppedTime (低) 打印暂停时长
- -XX:+PrintReferenceGC (重要性低) 记录回收了多少种不同引用类型的引用
- -verbose:class 类加载详细过程
- -XX:+PrintVMOptions
- -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 必须会用
- -Xloggc:opt/log/gc.log
- -XX:MaxTenuringThreshold 升代年龄,最大值 15
锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 … 这些不建议设置
Parallel 常用参数
-XX:SurvivorRatio
- -XX:PreTenureSizeThreshold 大对象到底多大
- -XX:MaxTenuringThreshold
- -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于 CMS,一般设为和 CPU 核数相同
-XX:+UseAdaptiveSizePolicy 自动选择各区大小比例
CMS 常用参数
-XX:+UseConcMarkSweepGC
- -XX:ParallelCMSThreads CMS 线程数量
- -XX:CMSInitiatingOccupancyFraction 使用多少比例的老年代后开始 CMS 收集,默认是 68%(近似值),如果频繁发生 SerialOld 卡顿,应该调小,(频繁 CMS 回收)
- -XX:+UseCMSCompactAtFullCollection 在 FGC 时进行压缩
- -XX:CMSFullGCsBeforeCompaction 多少次 FGC 之后进行压缩
- -XX:+CMSClassUnloadingEnabled
- -XX:CMSInitiatingPermOccupancyFraction 达到什么比例时进行 Perm 回收
- GCTimeRatio 设置 GC 时间占用程序运行时间的百分比
-XX:MaxGCPauseMillis 停顿时间,是一个建议时间,GC 会尝试用各种手段达到这个时间,比如减小年轻代
G1 常用参数
-XX:+UseG1GC
- -XX:MaxGCPauseMillis 建议值,G1 会尝试调整 Young 区的块数来达到这个值
- -XX:GCPauseIntervalMillis ?GC 的间隔时间
- -XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着 size 增加,垃圾的存活时间更长,GC 间隔更长,但每次 GC 的时间也会更长 ZGC 做了改进(动态区块大小)
- G1NewSizePercent 新生代最小比例,默认为 5%
- G1MaxNewSizePercent 新生代最大比例,默认为 60%
- GCTimeRatio GC 时间建议比例,G1 会根据这个值调整堆空间
- ConcGCThreads 线程数量
- InitiatingHeapOccupancyPercent 启动 G1 的堆空间占用比例