GC调优之响应时间优先
上一节简单介绍了GC调优的吞吐量优先,详见:https://blog.csdn.net/Winner941112/article/details/102679614,这一节将会考虑响应时长进行调优。
响应时间优先
依然使用之前的程序来模拟生产上的用户请求,但是改为每100毫秒创建1000线程,每个线程创建一个512kb的对象,观察GC情况,场景应该为响应时长优先。模拟代码如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 启动程序,模拟用户请求
* 每100毫秒创建1000线程,每个线程创建一个512kb的对象,最多100毫秒内同时存在1000线程,并发量1000/s,吞吐量6000/s,查看GC的情况
*
*/
@SpringBootApplication
public class JvmAdjust {
public static void main(String[] args) {
SpringApplication.run(JvmAdjust.class, args);
System.out.println("开始启动服务..........");
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(()->{
new Thread(()->{
for (int i = 0; i < 1000; i++){
try {
byte[] tmp = new byte[1024 * 512]; // 专门创建512kb的小对象
Thread.sleep(new Random().nextInt(100)); // 随机睡眠100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}, 100, 100, TimeUnit.MILLISECONDS);
}
}
将程序打包上传至Linux服务器运行:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc1.log -jar classloader-jvm-2.0.7.RELEASE.jar,通过:jstat -gc -h10 $(jcmd | grep “classloader-jvm-2.0.7.RELEASE.jar” | awk ‘{print $1}’) 1000 进行实时监控,监控如图:
从图中可以看到,在10s内发生了102次YGC,耗时0.794s;发生了FullGC 1次,耗时0.039s,总gc耗时0.833s。
GC调优
1.并行收集器
1.1 使用两个线程运行程序:-XX:ParallelGCThreads=2
运行启动命令:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc2.log -XX:ParallelGCThreads=2 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了103次YGC,耗时0.771s;发生了FullGC 1次,耗时0.039s,总gc耗时0.809s。
1.2 增大线程数量:-XX:ParallelGCThreads=4
运行启动命令:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc3.log -XX:ParallelGCThreads=4 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了104次YGC,耗时0.877s;发生了FullGC 1次,耗时0.048s,总gc耗时0.925s。
1.3 减少线程数量:-XX:ParallelGCThreads=1
运行启动命令:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc4.log -XX:ParallelGCThreads=1 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了101次YGC,耗时0.958s;发生了FullGC 2次,耗时0.127s,总gc耗时1.085s。
2.CMS标记清除收集器
2.1 使用两个线程: java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc5.log -XX:+UseConcMarkSweepGC -XX:ConcGCThreads=2 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了865次YGC,耗时3.263s;发生了FullGC 23次,耗时0.19s,总gc耗时3.453s。
2.2 将线程数量增加到4个:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc6.log -XX:+UseConcMarkSweepGC -XX:ConcGCThreads=4 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了187次YGC,耗时1.451s;发生了FullGC 17次,耗时0.197s,总gc耗时1.648s。
2.3 将线程数量减少到1个:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc7.log -XX:+UseConcMarkSweepGC -XX:ConcGCThreads=1 -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控结果如图:
从图中可以看到,在10s内发生了1602次YGC,耗时5.637s;发生了FullGC 26次,耗时0.162s,总gc耗时5.79s。
3.G1收集器
3.1 命令参数:java -Xmx1024m -Xloggc:/usr/local/jvmtest/gc8.log -XX:+UseG1GC -jar classloader-jvm-2.0.7.RELEASE.jar,实时监控如图:
从图中可以看到,在10s内发生了80次YGC,耗时1.085s;发生了FullGC 0次,耗时0s,总gc耗时1.085s。
总结:在通过尝试不同的垃圾收集器后,发现cms垃圾收集器收集次数最频繁,用时也是最多的,而耗时最短的是默认的gc收集器。在真实的生产环境中,影响性能的问题90%以上存在于业务代码中,JVM的一些参数大多数时候是不会进行调整的,并且,gc调优是一个逐步的过程,需要尝试多次来最终决定一个合适的参数。