JVM调优之内存优化与GC优化

JVM系列文章目录

初识JVM

深入理解JVM内存区域

玩转JVM对象和引用

JVM分代回收机制和垃圾回收算法

细谈JVM垃圾回收与部分底层实现

Class文件结构及深入字节码指令

玩转类加载和类加载器

方法调用的底层实现

Java语法糖及底层实现

GC调优基础知识工具篇之JDK自带工具

GC调优基础知识工具篇之Arthas与动态追踪技术

JVM调优之内存优化与GC优化

JVM调优之预估调优与问题排查

JVM调优之玩转MAT分析内存泄漏

直接内存与JVM源码分析



前言

本文基于JDK1.8,主要介绍堆内存大小调整堆JVM效率堆影响。



Apache Bench

我在此次调优中使用该压测工具进行测试,这里详细安装操作就不进行介绍了(我这边是因为mac自带测试方便再加上这个东西也比较清,当然各位也可以用其他测试工具,如jmeter),各位可以参考官网



内存调优与GC调优


测试工程

我这里简单的构造一个spring boot 工程,只有简单的对象构建存储,这样就可以抛除代码性能问题等一系列其他干扰因素,只关心内存相关信息。

工程结构:
在这里插入图片描述

测试接口代码:

package cn.abfeathers.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: abfeathers
 * @date: 2021/3/20
 * @description: 测试接口
 */
@RestController
@RequestMapping("/jvm")
public class TestController {
    @RequestMapping("/heap")
    public String test(){
        List<Byte[]> list = new ArrayList<Byte[]>();
        Byte[] b = new Byte[1024*1024];
        list.add(b);
        return "success";
    }
}

JVM默认内存分配下进行压测

本来想再做一组1000并发和10000并发的测试的,mac 自带的ab有一些bug,高并发没办法测试,本来准备新装一个ab,然后替换原来的,但是M1改不了权限,就能拿这个200并发的意思一下了。我这边强制将堆空间压缩到了200M -Xms200m -Xmx200m

  1. 启动测试工程。
    在这里插入图片描述

  2. 先进行一轮测试预热。
    我们采用10的并发,测试10万请求。

    ab -c 10 -n 10000 -k http://localhost:8080/jvm/heap
    

    在这里插入图片描述
    查看GC情况

    jstat -gc 1613 5000 20 | awk '{print $13,$14,$15,$16,$17}'
    

    在这里插入图片描述

  3. 200并发,测试10万请求。
    在这里插入图片描述

    测试结束,GC情况:
    在这里插入图片描述

    测试结果:

    • 平均吞吐量大约在:503/s。
    • JVM平均处理请求时间:1.986ms。
    • JVM发生GC次数:1996次YGC,耗时11.331s;FGC次数253,耗时5.988s,总GC耗时17.32s。

优化一

这次我将JVM内存增大到500M -Xms500m -Xmx500m,再来进行100并发,10万请求的压测。

  1. 重新启动启动测试工程。
    在这里插入图片描述

  2. 再次采用10的并发,测试10万请求进行预热。

  3. 200并发,测试10万请求。
    在这里插入图片描述
    在这里插入图片描述

    • 平均吞吐量大约在:1772/s。
    • JVM平均处理请求时间:0.564ms。
    • JVM发生GC次数:519次YGC,耗时3.494s;FGC次数21,耗时0.476s,总GC耗时4.113s。

优化二

这次我不光将JVM内存增大到500M,还调整堆空间各个分区的比例8:1:1,扩大Eden区 -Xms500m -Xmx500m -XX:SurvivorRatio=8,再来进行100并发,10万请求的压测。

  1. 重新启动启动测试工程。
    在这里插入图片描述

  2. 再次采用10的并发,测试10万请求进行预热。

  3. 200并发,测试10万请求。
    在这里插入图片描述
    在这里插入图片描述

    • 平均吞吐量大约在:2463/s。
    • JVM平均处理请求时间:0.406ms。
    • JVM发生GC次数:313次YGC,耗时2.126s;FGC次数22,耗时0.430s,总GC耗时2.555s。


调优总结

需要考虑一下四个方面

  • GC 频率
    高频的 FullGC 会给系统带来非常大的性能消耗,虽然 MinorGC 相对 FullGC 来说好了许多,但过多的 MinorGC 仍会给系统带来压力。

  • 内存
    这里的内存指的是堆内存大小,堆内存又分为年轻代内存和老年代内存。堆内存不足,会增加 MinorGC ,影响系统性能。

  • 吞吐量
    频繁的 GC 将会引起线程的上下文切换,增加系统的性能开销,从而影响每次处理的线程请求,最终导致系统的吞吐量下降。

  • 延时
    JVM 的 GC 持续时间也会影响到每次请求的响应时

其实我们在一般项目中,堆空间大小都不太大,而当高并发的时候实际上是需要一个大堆空间,但是光调大堆空间效果还不是最好的,我们还要合理的分配Eden区、From区、To区的大小,因为在高并发的场景下,大部分对象都是“一次性”用品,完全不需要回收。

而且一味的调大堆空间有时候也反而会使性能降低,因为单次 Minor GC 时间是由两部分组成:T1(扫描新生代)和 T2(复制存活对象)。当对象存活时间>MinorGC间隔时间的时候,而新生代内存比例又没有调整的话,就有可能降低性能。


推荐策略

  1. 新生代大小选择

    • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,新生代收集发生的频率也是最小的。同时,减少到达老年代的对象。
    • 吞吐量优先的应用:尽可能的设置大,可能到达 Gbit 的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合 8CPU 以上的应用.。
    • 避免设置过小。当新生代设置过小时会导致:1.MinorGC 次数更加频繁 2.可能导致 MinorGC 对象直接进入老年代,如果此时老年代满了,会触 发 FullGC.
  2. 老年代大小选择

    • 响应时间优先的应用:老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数.如果堆设置小了,可 以会造成内存碎 片,高回收频率以及应用暂停而使用传统的标记清除方式; 如果堆大了,则需要较长的收集时间.最优化的方案,一般需要参考以下数据获得:
      并发垃圾收集信息、持久代并发收集次数、传统 GC 信息、花在新生代和老年代回收上的时间比例。
    • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的新生代和一个较小的老年代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而 老年代尽存放长期存活对象。


上一篇:GC调优基础知识工具篇之Arthas与动态追踪技术

下一篇:JVM调优之预估调优与问题排查

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值