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平均时间等等,都能给你调优带来很大帮助

Last Updated: