JVM内存调优实战案例
目录
这篇文章通过一个简单的案例来向大家介绍一下几种JVM内存调优方式,希望大家可以通过实践将理论知识形成一个闭环,纸上谈兵是最没有意义的
一、测试案例
该接口 随机产生不同大小的对象,最大4M
/**
* 内存调优测试
* @return
*/
@GetMapping("/tuningTest")
public String tuningTest(){
List<Byte[]> temp = new ArrayList<Byte[]>();
// 随机产生不同大小的对象,最大4MB
int i = new Random().nextInt(8)+1;
Byte[] bytes = new Byte[1024 * 512 * i];
temp.add(bytes);
return "Success";
}
二、测试过程
首先最大堆内存设置800M,初始堆大小也是800M,测试不同并发线程相同请求量的吞吐量以及相应时间
1. 10并发线程/50000请求量
结果(吞吐量:531 平均响应时间:17)
2.20并发线程/50000请求量
结果(吞吐量:566平均响应时间:39)
3.40并发线程/50000请求量
结果(吞吐量:530平均响应时间:78)
4.50并发线程/50000请求量
结果(吞吐量:516平均响应时间:90)
从上述结果我们可以看到随着并发数量的上升,吞吐量很快就上不去了,而且响应时间却在不断上升,我们以第三次结果来看看GC 情况(多次测试结果不一定一样,因为接口产生的对象大小随机)
YGC : 3481次 平均时间:73.9ms FGC :28次 平均时间:3.3ms
三、优化方案
1.增大堆内存,减少FULL GC 频率
我们把堆内存设置到1.2G,以上述第三种情况测试,因为上述第三种吞吐量上不去了
结果:(吞吐量:602 平均响应时间:79 FULL GC次数:28 )
可以看到虽然响应时间没变但吞吐量上去了,而且FULL GC 次数大大减小了
2.增大年轻代,减少YGC 频率
堆内存800M不变,年轻代设置到600M
结果:(吞吐量:914平均响应时间:49 YGC 次数:1828 49ms FULL GC次数:28 )
虽然FGC次数没变,但吞吐量达到了惊人的914 响应时间也降了下来,YGC次数几乎减少了一半
3.调大堆内存和年轻代
堆内存1200M,年轻代900M
结果:(吞吐量:958平均响应时间:41 YGC 次数:756 15ms FULL GC次数:23)
4.设置 Eden、Survivor 区比例
在 JDK1.8 中,默认是开启 AdaptiveSizePolicy 的,则每次 GC后都会重新计算 Eden、From Survivor 和 To Survivor 区的大小,计算依据是 GC 过程中统计的 GC 时间、吞吐量、内存占用量。我们可以通过 -XX:-UseAdaptiveSizePolicy 关闭该项配置,或显示运行 -XX:SurvivorRatio=8 -XX:NewRatio=2 将 Eden、Survivor 的比例固定。
这个比例的设置情况太多了, 我就不再演示了
5.根据需求,设置垃圾回收器
这个我也就不再演示了, 因为到了这一步都会基于场景优化了
6.调整各种阈值参数
不演示,这里DEMO情况过于简单,容易产生误导
总结
调优是一个需要基于场景、基于某种指标,大量测试得出来的一种结论,没有什么万金油的配置,实践大于一切,在复杂的业务场景下,你的理论知识终究是纸上谈兵,调优终究还是内存的利用,以及对项目的熟悉,还有业务场景的适配。了解对象在JVM的存活周期、了解在JVM升代的过程、了解大对象产生的场景、业务场景、并发量、GC频率、GC平均时间等等,都能给你调优带来很大帮助