服务端调优与JVM调优

一、安装Jmeter

  下载地址:http://jmeter.apache.org/download_jmeter.cgi

  下载后直接进入bin目录,直接运行jmeter即可,我这里用得是mac,直接sh jmeter

  1、调整语言

    Options--Choose Languages--Chinese(Simple lified)

  2、添加jmeter-plugin插件

    下载地址:链接: https://pan.baidu.com/s/1RXD6JnT5LJx5mBvxmUf7JQ 提取码: 90ni

    下载后文件为plugins-manager.jar格式,将其放入jmeter安装目录下的lib/ext目录,然后重启jmeter,即可。

  3、添加jpgc插件

    这个插件可以监测TPS,RT等重要指标。

    选项-plugin Manager --jpgc - Standard Set,安装后重启。

二、压测

  写一个简单的demo

    @RequestMapping(value = "/goods/detail/nocache/{seckillId}",method = RequestMethod.GET)
    public TbSeckillGoods findOneNoCache(@PathVariable Integer seckillId){
        TbSeckillGoods  seckillGoods = seckillGoodsMapper.selectByPrimaryKey(id);
    }

  运行:

nohup java -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

  这就是一个最简单的使用主键查询。

   1、添加测试项(01-秒杀系统测试方案)

    测试方案--添加--线程(用户)--线程组,设置测试接口名称(01-商品详情测试方案)。

    测试接口--添加--取样器--HTTP请求(01-商品详情HTTP请求)

    测试接口--添加--监听器--聚合报告(02-商品详情聚合报告)

    测试接口--添加--监听器--查看结果树(03-商品详情结果树)

    测试接口--添加--监听器--jp@gc - Transactions per Second (04-商品详情接口TPS)

    测试接口--添加--监听器--jp@gc - Response Times Over Time(05-商品详情响应时间)

      

 

  2、验证

    点击开始,然后查看结果树中的响应数据,可以看到正常返回,验证无误。

      

 

   3、正式压测

    修改测试方案中的线程数和循环次数,这里模拟20W个样本,线程数为4000,启动时间为5秒,循环次数为50次,这几个参数的意思是:在5秒内顺序启动4000个线程,这个操作循环50次。这50次循环是并行执行的。

    (1)TPS

      

 

     如果服务的TPS存在明显的抖动,说明要么服务有问题,要么网络有问题。TPS性能曲线不能出现明显抖动(当前条件当测试演变比较稳定),查看服务问题。

     这里再说明一下TPS和QPS,TPS ,QPS,吞吐量:大多数情况下,单独一个接口: TPS = QPS = 吞吐量(每秒请求数)

      

 

    (2)RT

      

 

     (3)聚合报告

      

 

       # 样本: 测试的样本数 20w

      # 平均值: 所有的请求从发送请求到请求结束所消耗的平均时间

      # 中位数:50%的请求在713ms之内响应结束

      # 90%百分位:90%的请求在782ms之内响应结束

      最小值: 请求消耗的最小时间

    (4)测试参数

      

 

三、服务端调优

  1、分析与调优

  查看线程数:pstree -p 进程号 | wc -l

pstree -p 3769 | wc -l

 

  如果没有命令,需要安装pstree:

yum -y install psmisc 

 

      

 

  经过测试,发现服务器只产生了 200 个线程来处理业务请求,增加线程数,可以提升吞吐能力;考虑 cpu 处理能力的问题,cpu 上下文切换耗时,抢占 cpu 等待问题;考虑 cpu 性能损耗问题,使用 top 查看即可;

  Springboot内置的Tomcat配置信息在:spring-boot-autoconfigure-2.2.5.RELEASE.jar中的spring-configuration-metadata.json中

      

  

  其中比较重要的三个配置项:最大连接数、服务队列、最大线程数,三者的关系如下图所示:

    首先使用等待队列接收请求,然后创建连接,然后执行业务,如果当最大连接数,等待队列都已经满了,tomcat 服务就会拒绝连接。

      

   (1)最大连接数:

      

 

   (2)服务队列

      

 

   (3)最大线程数

      

 

   由于部署应用的服务器是4C8G,单个线程默认为200个线程,这里4个CPU,将线程数调整为800。

server:
  port: 9000
  tomcat:
    # 编码格式
    uri-encoding: utf-8
    # 服务队列
    accept-count: 1000
    # 最大连接数
    max-connections: 20000
    # 最大线程数
    max-threads: 800
    # 初始化线程,应对突发流量
    min-spare-threads: 10

 

  2、验证压测

      

 

   经过 20w 个样本的测试,发现 TPS 没有发生任何的变化,TPS = 2700

      

 

   可以发现在服务器最大线程数增加了 4 倍,理论上来说,TPS 一定会增加 4 倍,但是根据实际测试结果,发现 TPS 没有任何的变化,这是因为没有执行任何业务,不是一个耗时的操作,主键查询:0-10ms 结束,无需要对一个不耗时操作进行调优。

  结论:调优应该是对于耗时操作进行调优。

  3、模拟耗时操作调优

  模拟一个耗时操作

      public TbSeckillGoods findOne(Integer id){
            //直接从数据库查询
            //主键查询 : cpu不耗时操作
            TbSeckillGoods  seckillGoods = seckillGoodsMapper.selectByPrimaryKey(id);
            //计算对象大小
            //模拟程序耗时操作,如果方法是一个笔记耗时的操作,性能优化非常有必要的!!
              try {
                  Thread.sleep(1000);
                  LOGGER.info("模拟耗时操作,睡眠1s时间!");
                  LOGGER.info("对象占用jvm堆内存大小: {}", RamUsageEstimator.humanSizeOf(seckillGoods));
            } catch (InterruptedException e) {
                  e.printStackTrace();
            }
            //返回结果
            return seckillGoods;
      }

 

   在未调优的情况下进行压测:没有优化之前,对于一个耗时操作来说,TPS = 200

      

 

  调优后压测:调优之后的 TPS 情况(线程数增加:800) -- 理论上: cpu 没有任何压力,TPS 应该会获得倍数的提升,提升 4 倍

      

 

   经过测试:发现 TPS 有了 4 倍的提升,tps = 800

  4、Keepalive

  客户端和服务端连接的时候,为了防止频繁建立连接,释放连接,浪费资源,这样就好损耗的性能,造成性能严重下降;

       

   可以发现: jmeter connection : keep -alive 长连接

      

 

     查看连接数:

netstat -anp | grep ESTABLISHED | wc -l

  

   但是keepalive 长连接本身也会消耗大量资源,如果这些长连接不能及时释放,长连接将会占用大量资源,系统 TPS 就会上不去;因此对 keepalive 设置一个比较合理的连接数;另外: 长连接也必须及时释放,没有请求使用这个链接,这个链接一段时间必须释放;

  可以在代码中定制tomcat服务器的长链接设置。

@Component
public class WebServerConfig implements WebServerFactoryCustomizer<ConfigurableWebServerFactory>{
    // 定制tomcat服务器
    @Override
    public void customize(ConfigurableWebServerFactory configurableWebServerFactory) {
        ((TomcatServletWebServerFactory)configurableWebServerFactory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
            // 获取tomcat连接器
            @Override
            public void customize(Connector connector) {
                // 获取protocol
                Http11NioProtocol protocolHandler = (Http11NioProtocol) connector.getProtocolHandler();
                // 如果keepalive连接30s,还没有人使用,释放此链接
                protocolHandler.setKeepAliveTimeout(30000);
                // 允许开启最大长连接数量,4cpu,8gb
                protocolHandler.setMaxKeepAliveRequests(10000);
            }
        });
    }
}

 

 四、线上问题分析

(一)问题分类

  发现问题,解决问题 (测试时候发现一些问题,修复,调试,调优 , 更多的时候: 上线以后发现一些问题,解决问题)系统出现问题分类:

  1、系统异常

    CPU 占用率过高,磁盘满了,磁盘 IO 阻塞,网络流量异常等等问题

    排查指令:TOP,free,dstat , pstack , vmstat , strace 获取异常信息,排查系统异常

  2、业务异常

    流量太大系统扛不住,耗时长,线程死锁,并发问题,频繁 full gc ,oom 等等

    排查: top, jstack ,pstack , strack 日志

(二)问题排查

  1、TOP命令

    Top 指令监控 cpu 使用情况,根据 cpu 使用分析系统整体运行情况

      

 

     关注性能指标: load average : 1 分钟之内 cpu 平均使用率,5 分钟之内 cpu 平均使用率,15分钟之内 cpu 平均使用率;如果1分钟之内cpu使用率较高,但是5分钟或15分钟内较低,有可能是短暂的流量激增导致,可以暂时不用关心。

    单核心 cpu:

      1)load average < 1 , 表示 cpu 毫无压力,比较空闲,运行流畅

      2)load average = 1 , 表示 cpu 刚刚被占满,没有可供提供的 cpu 资源

      3)load average > 1 , 表示 cpu 已经满负荷运作,线程处于阻塞等待状态,等待 cpu资源

      4)load average > 5 , 表示 cpu 已经处于超负荷运作,线程已经大面积阻塞,必须进行优化处理;

    4 核心 cpu:

      1)load average < 4 , 表示 cpu 毫无压力,比较空闲,运行流畅

      2)load average = 4 , 表示 cpu 刚刚被占满,没有可供提供的 cpu 资源

      3)load average > 4 , 表示 cpu 已经满负荷运作,线程处于阻塞等待状态,等待 cpu资源

      4)load average > 10 , 表示 cpu 已经处于超负荷运作,线程已经大面积阻塞,必须进行优化处理;

  2、Free命令

    Free 排查线程问题:内存问题很多时候引起 cpu 较高的原因。

      

 

  3、磁盘

    Df 指令查看磁盘问题,查看磁盘使用情况,有时候服务出现问题,其实就是磁盘出现问题。

      

 

  4、网络

    Dstat 命令: 集成 vmstat , iostat , netstat ,工具完成任务,-c 查看 cpu 使用情况, -d 查看磁盘读写,-n 网络状态 -l 显示系统负载

    详细的参数展示可以参考下这篇文章:http://www.yishimei.cn/network/850.html

      

 

 五、JVM调优概述

(一)为什么调优

  1、调优的目的

    调优的目的就是为了提升项目性能,避免项目出现一些不可预知的 bug(频繁 fullgc,oom);

    (1)jvm 堆内存空间对象太多(Java 线程,垃圾对象),导致内存空间被占满,程序跑不动 –性能严重下降,通过调优:及时释放内存空间

    (2)垃圾回收线程太多(频繁的垃圾回收,垃圾回收线程也会占用 CPU,内存资源),必然导致程序性能下降,通过调优:防止频繁的 gc

    (3)垃圾回收导致 STW (stop the world : 停止整个世界 --- 整个业务执行全部暂停),通过调优:尽可能减少 gc 次数

      

  2、JVM 调优的本质

    jvm 调优本质就是(对内存空间的调优)及时是否垃圾对象占用的内存空间,让程序性能得以提升,让其他业务线程可以使用内存空间;

  3、如果把 jvm 堆内存设置的足够大(无限大),是不是不需要垃圾回收?

    理论上确实可以考虑内存空间垂直扩容,解决 JVM 内存问题,但是同样存在问题:

      (1)成本问题 --- 高性能服务器

      (2)一旦触发垃圾回收 --- gc 时间比较长

      (3)微服务架构场景下(每一个服务都是一个微小,独立的服务) --- 水平扩展

      (4)寻址能力的问题(JVM 默认内存调优分配空间: 32GB,超过32G就不会进行调优):

          32 位操作系统: 2~32 = 4GB 内存空间 --- Java 对象 8 字节 指针压缩 : 4GB *8 = 32GB

          64 位操作系统 2~64 = 16384PB

    小结: JVM 内存空间设置,必须设置一个大小合适的内存空间,不能太大,也不能设置太小;

      (1)成本问题 --- 高性能服务器

      (2)一旦触发垃圾回收 --- gc 时间比较长

      (3)微服务架构场景下(每一个服务都是一个微小,独立的服务) --- 水平扩展

(二)JVM 调优原则

  1、gc 时间足够小(堆内存设置足够小)

    此原则就是让我们在设置堆内存空间时候,不要把内存空间设置的太多,防止 gc 消耗太多的时间;

  2、gc 的次数足够少(堆内存空间设置的足够大)

    GC 触发条件: 内存空间被占满,立即触发垃圾回收(ps,po),也就是说内存空间被占满一次,就发生一次 gc,占满多次就发生多次 gc;

  3、发生 fullgc 周期足够长(最好不要发生 fullgc)

    (1)metaspace 元数据空间大小设置合适 ,metaspace 一旦发生扩容,fullgc 就会发生

    (2)老年代空间设置一个合理的大小,防止 fullgc

    (3)尽量让垃圾对象在年轻代回收(90%)

    (4)尽量防止大对象的产生,一旦大对象多了以后,就可能发生 fullgc , 甚至 OOM

(三)JVM 调优原理

  1、JVM 调优本质:

     回收垃圾,及时释放内存空间。在 Java 堆内存中,那些没有被引用的对象就是垃圾(高并发模式下,大量的请求在内存空间中创建了大量的对象,这些对象不会主动消失,必须进行垃圾回收,当然 Java 语言自己提供了垃圾回收器,帮助我们回收垃圾,JVM 垃圾是自动进回收的)

    JVM 提供 2 种方法寻找垃圾:引用计数法和根可达算法

  2、垃圾清理算法:

    mark-sweep 标记清除算法、copying 拷贝算法、mark-compact 标记压缩算法。

    标记清除算法:

      过程:(1)使用根可达算法找到垃圾对象,对垃圾对象进行标记(仅仅是做一个标记);(2)对标记的对象做清除。

      优点: 简单,高效;

      缺点: 产生很多的内存碎片

    copying 算法 – 一开始把内存空间一分为二,分为 2 份大小相同的内存空间,一般内存空间作为备份使用。

      过程:(1)选择(寻址)存活对象;(2)把存活对象拷贝另一半空闲空间中,且是连续的空间;(3)把存活对象拷贝结束后,另一半空间中就是垃圾对象(全是垃圾对象),直接清除即可;

      优点:简单,内存空间是连续的,不会存在内存碎片

      缺点:内存空间的浪费

    mark-compact 标记压缩算法:

      过程:(1)标记垃圾(只标记,不清除);(2)再次扫描内存空间(未被标记的对象就是存活对象),找到存活对象,且把存活对象向一端移动(一端内存空间是连续的)--压缩,整理;(3)当存活对象全部被移动到一端后,另一端就是垃圾对象,直接清除即可

  3、垃圾回收器

    Java 语言提供了 10 种垃圾回收器:

      

 

   特点:

    (1)Serial(年轻代) Serial Old(老年代) , parNew(年轻代) CMS(老年代) , Parallel Scavenge(年轻代) Parallel Old(老年代) 都属于物理分代模型中垃圾回收器,年轻代,老年代模型,分别都使用不同的垃圾回收器;

    (2)G1 在逻辑上分代模型,使用非常方便;关于年轻代,老年代只需要使用 g1 一个垃圾回收器即可;

    (3)zgc zgc 是 jdk11 新加入的垃圾回收器,在试验阶段;

    (4)shenandoah openJDK 的垃圾回收器

    (5)epsilon 是 debug 使用的,在调试环境下,验证 jvm 内存参数设置的可行性;

    (6)Serial 和 Serial Old 串行化的垃圾回收器

    (7)parNew和CMS : 响应时间优先垃圾回收器组合 (fullgc 比较多,无法避免 --- cms)

    (8)parallel scavenge 和 parallel old : 吞吐量优先的垃圾回收器组合

  常用垃圾回收器组合:

    (1)Serail + Serial Old : 串行化的垃圾回收器,适合单核心的 cpu 服务器

    (2)parNew + CMS : 响应时间有些的垃圾回收器,并行,并发垃圾

    (3)Parallel Scavenge+Parallel Old : 并行垃圾回收器

    (4)g1 逻辑上分代垃圾回收器

  4、垃圾回收器原理

  (1)Serial+Serial Old

    Serial : 年轻代垃圾回收器,单线程垃圾回收器; Serial Old : 老年代的垃圾回收器,也是一个单线程的垃圾回收器,适合单核心 cpu 情况。

      

 

     注意:1、stw : 任何垃圾回收器都无法避免 stw(stop the world : 当进行 gc 的时候,整个业务线程必须暂停),如果 stw 时间过长,或者 stw 发生次数过多,都会影响程序的性能;2、垃圾回收线程: 多线程,单线程,并发,并行

  (2)Parallel Scavenge + Parallel Old

    Parallel Scavenge+Parallel Old : 并行垃圾回收器,吞吐量优先的垃圾回收器组合,是 JDK8 默认垃圾回收器组合;(并行: 同一个时候,同时执行,并行;并发: 在一段时间内,多个线程抢占式交叉执行,叫做并发模式;)

    PS + PO : 垃圾回收器进行垃圾回收器的时候,采用多线程模式回收垃圾

      

 

   (3)parNew+CMS

    parNew : 年轻代垃圾回收器,并行的垃圾回收器;

      

 

     CMS : 并发垃圾回收器,响应时间优先垃圾回收器

      

 

         初始化标记:(寻址垃圾比较耗时)只标记跟根对象相关联的对象,因此耗时比较少,且采用单线程模式标记

        并发标记: (寻址垃圾比较耗时)为了防止 gc 时候 stw, 因此使用并发模式标记垃圾,也就是说让业务线程和 gc 标记线程交叉执行;

        重新标记: 重新标记漏标的对象,此时漏标对象比较少,因此耗时比较少

        并发清理:(清理垃圾比较耗时) 采用并发模式清理垃圾,业务线程和清理垃圾线程交叉执行,以此减少业务线程停顿的时间;

  (4)G1

    G1 jdk11 成为了默认的垃圾回收器;采用了分区的思想;对内存进行逻辑上分代;采用RememberSet 集合记录每一个 region 区域

  5、内存分代模型

      

 

  通过内存分代模型: 大多数对象都会在年轻代被回收(90%+),超过 15 次没有被回收的对象就进入 old 区域。垃圾回收器触发时机:

    (1)ps + po : 当内存空间(eden,old)被占满后,触发垃圾回收器

    (2)cms 垃圾回收器

        jdk1.5 : 68% ,当 eden 区域被装载到 68%的时候,触发垃圾回收器

        jdk1.6+ : 92% 触发垃圾回收器

六、JVM调优实战

(一)调优实战

  JVM 调优的本质就是 GC(垃圾回收,及时释放掉内存,提供给其他的线程使用—并发模式),每次 GC 的时候,导致业务线程 STW, 因此必须进行综合的考量:频繁的 gc 也会导致性能下降

  JVM 调优其实就是选择一个合适的垃圾回收器(在不同的场景下),设置一些合理垃圾回收参数(参考 JVM 内存模型),分配合理的内存(gc 时间少,次数少,jvm 内存既不能太大,也不能太小)。因此调优的实践本质就是设置 jvm 参数

  1、典型设置

  典型的参数设置: 服务器配置 —> 4cpus,8GB

    1、-Xmx4000m 设置 JVM 堆内存最大值(经验值设置:3500m --- 4000m,内存大小的设置,没有一个固定值,根据业务场景实际情况,根据压力测试运行情况进行调试,在上线后,进行调试)。例如: 对象 1 占用内存 128KB;对象 2 占用内存 300KB

    2、-Xms4000m 设置 JVM 初始化内存(必须和 Xmx 最大内存设置一致,防止内存抖动,损耗性能)

    3、-Xmn2g 设置年轻代大小(eden ,s0 ,s1)

    4、-Xss256k 设置线程堆栈大小,jdk1.5+版本线程堆栈默认 1MB, 相同内存情况下,线程堆栈越小,操作系统有能力创建更多线程,系统性能将会越好;

  查看对象占用JVM堆内存大小:

RamUsageEstimator.humanSizeOf(seckillGoods)

 

  然后调整代码的执行命令,设置上面的参数:

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

      

 

 

   根据压力测试结果(20w 样本),发现 JVM 内存参数设置后,没有任何优化效果(变化),原因就在于在这个业务场景下没有发生频繁的 gc(gc 导致 stw ,stw 最终影响性能);那我们是根据什么指标判断 jvm 调优的参数是合适的,是否能提到调优的效果的呢:

    (1)发生几次 GC ,GC 次数是否减少,是否频繁的发生 GC, 发送 GC 时间

    (2)业务线程执行时间比例 (业务线程执行时间超过 95%, 5%时间 gc 时间,此时不需要调优)

    (3)是否发生 fullgc (fullgc 耗时比较长,性能影响比较大,fullgc 发生次数)

    (4)oom

   2、GC日志

    JVM 在 gc 的时候输出日志,把日志写入文件中,使用相应工具(gceasy.io)对日志进行分析,分析 jvm 堆内存调优的结果是否合适。

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

     通过 jvm 相关参数,输出 gc 日志,通过分析 gc 日志判定 gc 的参数设置是否合理;从而给调试 jvm 堆内存设置提供参考依据;

    拿到 gc 日志后,可以在  gceasy.io  网站进行在线分析

    (1)内存使用情况

        用于展示各分代的总内存和最大使用内存。

      

 

 

     (2)执行时间对比

      这个是关键的是否需要JVM调优的判断标准,如果业务时间占比大于95%,就无需调优。

      

   

     (3)GC耗时占比

      

 

     (4)发生GC的时间点和次数

        根据内存分布情况,old 最大使用 157mb , 而在此时却发生了 fullgc ,很明显这是不正常的,必须进行调试,规避不正常的 fullgc

      

 

     (5)GC统计

      

 

   3、fullGC问题

    20w 样本测试后,old 区域使用最大值 157mb, old 区域分配的内存是 1.9gb , 却发生了 full gc,这肯定不正常的,原因: metaspaceSize 云数据空间发生了扩展,导致了 fullgc 的发发生;metaspace 发生一次扩容,就会发生一次 fullgc(可以查看gc日志,可以看到元空间占用内存在变大)

jstat -gcutil 11811
jstat -gc 11811 2s 3
java -XX:+PrintFlagsFinal -version | grep MetaspaceSize

 

      

 

     根据 metaspace 扩容阀值的大小,看出超过 20m,就会发生第一次扩容(gc日志);

       

 

     Metaspace 发生了扩容(gc日志):

      

 

     解决方案: 观察 gc 日志,看 metaspace 最大使用空间是多少,然后把 metaspace 空间大小设置为比这个值大即可;

        

 

     根 据 上 图 所 示 , metaspace>55.11mb ; metaspace = 256m , 完 全 够 用 的 ;-XX:MaxMetaspaceSize=500m :定义最大的元数据空间,此时如果 metaspace 装不下,就会发生 fullgc ,同时 oom;

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:MetaspaceSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

     查看日志优化情况,看 fullgc 是否发生:

       

 

     可以发现此时 fullgc 消失了,说明解决问题:

      

 

   4、Yong&old

     年轻代,老年代大小比例的设置根据业务情况进行灵活的设置,无非就是 yong 空间设置大些,尽量让对象在年轻被回收;或者是老年代设置大些,在一些大对象,老年代对象比较多的情况下;

    设置方案: -XX:NewRatio = 4 ,年轻代 : 老年代比值 = 1:4,预测结果: 年轻代空间进一步缩小(更频繁的 yong gc – 耗时少),老年代的空间进一步增大(尽量防止 fullgc 的发生 – 不耗时)

    分配结果: 1、yong : old = 1:4 (4000MB) 800MB:3200MB , yong memory 变得更小了,oldmemory 变得更大了;2、-Xmn2g 直接设置年轻代大小,剩下的就是老年代的;

    修改配置:将Xmn删除(-Xmn2g),新增NewRatio配置项(-XX:NewRatio=4)

nohup java -Xmx4000m -Xms4000m -XX:NewRatio=4 -Xss256k -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

       

 

     Yong gc 发生的次数更多了,但是 gc 时间更加快速了,但是总消耗的时间变多了;

    但是这种也不是不能这样设置,这是一个需要权衡的结果,使用场景: 防止发生 fullgc(fullgc 有一些,消耗时间)

      

 

   5、Eden & s0 & s1

    尽量让对象在年轻代被回收(大多数对象都在年轻代被释放内存)官方推荐设置: eden : s0 : s1 = 8:1:1 设置方式: -XX:SurvivorRatio = 8。

 (二)垃圾回收器组合

  1、吞吐量优先

    并行垃圾回收器:

      年轻代: parNew , Parallel Scavenge

      老年代: Parallel old

    对于吞吐量优先的垃圾回收器: 使用常用组合: ps + po ,此组合是 jdk1.8 默认的垃圾回收器

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:MetaspaceSize=256m -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

  2、响应时间优先

    响应时间优先组合: parNew + CMS 垃圾回收器 ,响应时间优先主要是对 CMS(老年代垃圾回收器)来说的,适用于:老年代发生 fullgc 比较多的情况,且无法避免 fullgc;

    -XX:+UseParNewGC : 年轻代垃圾回收器,并行的垃圾回收器

    -XX:+UseConcMarkSweepGC: 老年代的垃圾回收器,并发的垃圾回收器(业务线程交叉执行)

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:MetaspaceSize=256m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

  3、G1 组合

  XX:+UseG1GC : 表示使用 g1 垃圾回收器; 使用更加简单,逻辑上分代模型;

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:MetaspaceSize=256m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar jshop.jar --spring.config.addition-location=application.yaml > shop.log 2>&1 &

 

   可以发现: 垃圾回收次数比较少的,但是总耗时比较长;

       

 

    

posted @ 2021-10-26 11:01  李聪龙  阅读(546)  评论(0编辑  收藏  举报