1.关于调优:
1.1调优的目的是什么?
jdk1.8默认的垃圾收集器:新生代Parallel Scavenge 和 老年代Parallel Old,这种收集器的特点是并行,但是在垃圾收集时会阻塞工作线程。当阻塞工作线程的时候,系统页面反应就会变慢。
所以jvm调优的目的就是:
减少YoungGC,以减少代码停顿
减少FullGC,以减少代码停顿,老年代空间很大,Gc一次需要很长的时间,所以一天最多 FullGC 一次,最好在系统空闲期(如深夜);
1.2需要调什么?
代码角度:
缩短对象生命期,尤其是大对象
减少对象产生的数量
jvm 参数角度:
优化JVM参数以减少YGC/FGC次数。降低移动到老年代的对象数量,缩短Full GC执行时间(stop-the-world持续的时间)。
具体调优措施如下(按照重要性由高到低排序):
- 减少对象产生的数量;
- 选择合适的GC收集器;
- 调整新生代空间大小
- 调整老年代空间大小
1.减少对象产生的数量,尤其是大对象
GC产生的原因是heap内对象过多超过一定的大小导致。控制住对象的数量、大小,就控制住了GC的源头,从而保证了GC的性能。比如尽量少使用String,换用StringBuilder或StringBuffer
2.选择合适的GC收集器
不同的Gc收集器适用不同的场景,例如CMS是并发与并行的收集器,但是当资源不够用的时候,线程间切换会有开销,倒没有serial 单线程收集的快。
3. 调整新生代空间大小
在Oracle JVM中除了JDK 7及最高版本中引入的G1 GC外,其他的GC都是基于分代回收的。也就是对象会在Eden区中创建,然后不断在Survivor中来回移动。之后如果该对象依然存活年龄一般超过默认值15时,就会被移到老年代中。还有些对象,因为占用空间太大以致于创建时直接在老年代。老年代的GC较新生代会耗时更长,因此减少移动到老年代的对象数量可以降低full GC的频率。
4.调整老年代空间大小
通过缩小老年代空间的方式来降低Full GC执行时间,可能会面临OutOfMemoryError或者带来更频繁的Full GC。
通过增加老年代空间来减少Full GC执行次数,单次Full GC耗时将会增加。
2.线上调优实战
现象:一次线上问题,考试过程中考试页面反应很慢,发现内存,cpu占用过高
解决办法:
- 在线考试过程,通过top命令监控到线程cpu 和内存,发现占用很高
- top -Hp查看当前进程的各个线程运行情况,找出cpu运行过高的线程
- 通过 jstack 查看该线程主要进行的工作 jstack 10393|grep -A 10 11174
发现是"VM Thread" os_prio=0 tid=0x00007f871806e000 nid=0xa runnable,这里的 VM Thread 一行的最后显示 nid=0xa,这里 nid 的意思就是操作系统线程 id 的意思,而 VM Thread 指的就是垃圾回收的线程。
这里我们基本上可以确定,当前系统缓慢的原因主要是垃圾回收过于频繁,导致 GC 停顿时间较长。
- 查看当前gc情况: jstat -gcacuse 103931000
jstat -gc