【JVM】四、JVM优化-GC调优

传送门
【JVM】一、JVM体系结构
【JVM】二、JVM垃圾收集器
【JVM】三、JVM内存溢出问题分析查看
【JVM】四、JVM优化-GC调优

上一篇:【JVM】三、JVM内存溢出问题分析查看

一、GC日志分析

GC日志在线分析工具:https://gceasy.io 有一些功能是收费的;
该工具能帮助我们来分析GC日志的工具,通过分析GC日志,让我们去进行调优,但是该工具并不能完全诊断出应用的问题所在,我们在实际工作中会结合使用其他一些工具多维度诊断我们的应用程序;
首先我们需要打印出GC日志

 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:d:/logs/jvmgc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/logs/heapdump.hprof

附录:
https://heaphero.io 堆栈分析
https://fastthread.io 线程分析

GCViewer
https://github.com/chewiebug/GCViewer

在这里插入图片描述

下载下来就是一个jar,通过 java -jar gcviewer-1.36.jar 运行;
在这里插入图片描述

二、GC调优

1、如何选择垃圾收集器

优先调整堆的大小(最大最小-Xms、-Xmx)然后让JVM自己去选择;
如果内存小于100M,使用串行垃圾收集器;(一些嵌入式设备)
如果是单核CPU,并且没有停顿时间上的要求,采用串行垃圾收集或者让JVM自己选择;
如果允许停顿时间超过1秒,选择并行垃圾收集或者JVM自己选;
如果响应时间最重要,并且不能超过1秒,采用并发垃圾收集器;

2、并行垃圾收集器

-XX:+UseParallelGC 手动指定开启并行垃圾收集,Server模式下默认开启;
-XX:ParallelGCThreads=4 指定多少个GC线程,默认CPU>8 n=5/8,CPU<8 n=cpu数
并行垃圾收集有一个自适应的策略,会不断地扩容或缩容来满足停顿时间和吞吐量的要求;这个也不是最优的方案;
-XX:MaxGCPauseMillis
-XX:GCTimeRatio
它的动态内存调整可以通过参数控制
-XX:YoungGenerationSizeIncrement
-XX:TenuredGenerationSizeIncrement
-XX:AdaptiveSizeDecrementScaleFactor

3、CMS垃圾收集器

并发收集
低延迟、低停顿
老年代垃圾收集
JDK1.8之前大部分web应用都是采用该垃圾收集器;
CMS的调优参数:
-XX:ConcGCThreads 并发的GC线程数
-XX:UseCMSCompactAtFullCollection  FullGC后做压缩
-XX:CMSFullGCsBeforeCompaction  多少次FullGC后压缩一次
-XX:CMSInitiatingOccupancyFraction  触发FullGC
-XX:UseCMSInitiatingOccupancyOnly 是否动态调整
-XX:CMSScavengeBeforeRemark  FullGC前先YGC

4、G1垃圾收集器

新生代、老年代收集器;
在这里插入图片描述

把内存分成一个个的小块,一个小块称为一个Region;
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html

5、Young GC

新对象进入Eden区
存活对象拷贝到Survivor区
存活对象年龄达到阈值后晋升到Old区;(默认15岁)
以上几点在 并行垃圾收集、cms垃圾收集 上都是一样的;

6、Mixed GC

不是Full GC,回收Young区和部分Old区;
Mixed GC时会进行一次全局标记,当
-XX:InitiatingHeapOccupancyPercent=45时触发;

7、 垃圾收集器追求的两项指标

  • **停顿时间:**垃圾收集时候,应用程序被停顿了多少时间;
    -XX:MaxGCPauseMillis
  • **吞吐量:**垃圾收集花费的时间与应用程序运行的时间的比例;
    -XX:GCTimeRatio
    所有我们JVM调优的话,最好的情况的是 吞吐量最大,而停顿时间最小;

8、 GC调优目标

1、最小停顿时间;
2、最大吞吐量;

9、JVM调优的步骤

  • 1、打印GC日志;
  • 2、分析GC日志得出与性能相关的问题;
  • 3、调优JVM参数提升性能;
    此过程在实际生产中是反复调试的过程,通过不断地调整实现最优化,有时候不是一步到位的;
    打印GC日志:
    -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:d:/jvm/jvmgc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/jvm/heapdump.hprof -XX:+DisableExplicitGC
    其中 -XX:+DisableExplicitGC 禁止在代码中使用System.gc();

三、GC调优指南

源自官网:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ergonomics.html

1、首先给调优定下目标

  • 1、最大暂停时间目标
  • 2、吞吐量目标
  • 3、微调直达上述目标达到
    maximum pause time goal **-XX:MaxGCPauseMillis=n:**每次GC时程序暂停最多多少毫秒;
    throughput goal -XX:GCTimeRatio=n: 表示花费总时间百分之多少的CPU时间去运行程序;
    footprint goal:如果其他目标都达到了,那么首先减少heap size,直到前两个goal不再满足,然后再慢慢增加,直到满足前面两个goal;
    调优前的初始参数:
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -Xloggc:d:/logs/gc.log
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=d:/logs/heapdump.hprof
    在这里插入图片描述

2、G1调优建议

1、年轻代不要使用 -Xmn,-XX:NewRatio 显式设置大小,会覆盖暂停时间的目标;
2、暂停时间不要太严格,吞吐量目标是90%及以上;

3、 GC常用参数

3.1、堆、栈、元空间设置

  • -Xss:每个线程的栈大小
  • -Xms:初始堆大小,默认物理内存的1/64
  • -Xmx:最大堆大小,默认物理内存的1/4
  • -Xmn:新生代大小-XX:NewSize:设置新生代初始大小
  • -XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。
  • -XX:YoungGenerationSizeIncrement=30 年轻代动态扩容增量
  • -XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。
  • -XX:MetaspaceSize:设置元空间大小
  • -XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。

3.2、 垃圾回收日志信息打印

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:fifilename

3.3、 垃圾收集器设置

  • -XX:+UseSerialGC:设置串行收集器
  • -XX:+UseParallelGC:设置并行收集器
  • -XX:+UseParallelOldGC:老年代使用并行回收收集器
  • -XX:+UseParNewGC:在新生代使用并行收集器
  • -XX:+UseParalledlOldGC:设置并行老年代收集器
  • -XX:+UseConcMarkSweepGC:设置CMS并发收集器
  • -XX:+UseG1GC:设置G1收集器
  • -XX:ParallelGCThreads:设置用于垃圾回收的线程数

3.4、 并行收集器设置

  • -XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数;
  • -XX:MaxGCPauseMillis:设置并行收集最大暂停时间
  • -XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

3.5、 CMS收集器设置

  • -XX:+UseConcMarkSweepGC:设置CMS并发收集器
  • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
  • -XX:ParallelGCThreads:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
  • -XX:CMSFullGCsBeforeCompaction: 设定进行多少次CMS垃圾回收后,进行一次内存压缩
  • -XX:+CMSClassUnloadingEnabled: 允许对类元数据进行回收
  • -XX:UseCMSInitiatingOccupancyOnly: 表示只在到达阀值的时候,才进行CMS回收;
  • -XX:+CMSIncrementalMode: 设置为增量模式。适用于单CPU情况
  • -XX:ParallelCMSThreads: 设定CMS的线程数量
  • -XX:CMSInitiatingOccupancyFraction: 设置CMS收集器在老年代空间被使用多少后触发
  • -XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理

3.6、G1收集器设置

  • -XX:+UseG1GC:使用G1收集器
  • -XX:ParallelGCThreads:指定GC工作的线程数量
  • -XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区
  • -XX:GCTimeRatio:吞吐量大小,0-100的整数(默认9),值为n则系统将花费不超过1/(1+n)的时间用于垃圾收集
  • -XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
  • -XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
  • -XX:G1MaxNewSizePercent:新生代内存最大空间
  • -XX:TargetSurvivorRatio:Survivor填充容量(默认50%)
  • -XX:MaxTenuringThreshold:最大任期阈值(默认15)
  • -XX:InitiatingHeapOccupancyPercen:老年代占用空间超过整堆比IHOP阈值(默认45%),超过则执行混合收集
  • -XX:G1HeapWastePercent:堆废物百分比(默认5%)
  • -XX:G1MixedGCCountTarget:参数混合周期的最大总次数(默认8)

3.7、 常用JVM参数

  • -Xms
  • -Xmx
  • -XX:NewSize -XX:MaxNewSize 新生代大小 新生代最大大小
  • -XX:NewRatio 新生代与老年代的比例 -XX:SurvivorRatio eden区与s区的比例
  • -XX:MetaspaceSize -XX:MaxMetaspaceSize 元空间的大小 元空间的最大大小
  • -XX:+UseCompressedClassPointers 元空间是否使用类指针压缩,使用的话该区域默认占用1G大小
  • -XX:+UseCompressedOops ,OOP =“ordinary object pointer”普通对象指针,压缩对象指针,起到节约内存占用的效果,原理,解释器在解释字节码时,植入压缩指令(不影响正常和JVM优化后的指令顺序)。具体逻辑是,当对象被读取时,解压,存入heap时,压缩;
  • -XX:CompressedClassSpaceSize 设置元空间的类指针压缩区域的大小;
  • -XX:InitialCodeCacheSize 设置初始CodeCache大小
  • -XX:ReservedCodeCacheSize 用于设置code cache的最大大小,通常默认是240M
  • 对象分配优先在Eden区分配
  • 大对象直接进入老年代:-XX:PretenureSizeThreshold 用来指定超过这个大小的对象直接放入老年代;
  • 长期存活的对象进入老年代:-XX:MaxTenuringThreshold -XX:+PrintTenuringDistribution -XX:TargetSurvivorRatio
  • -XX:MaxTenuringThreshold
  • 设置的是年龄阈值,默认15
  • -XX:+PrintTenuringDistribution
  • 打印一下对象的年龄信息
  • -XX:TargetSurvivorRatio
  • 设定survivor区的目标使用率,默认50
  • -XX:+UseG1GC 开启使用G1
  • -XX:G1HeapRegionSize=n region的大小,1-32M之间,最大2048个
  • -XX:MaxGCPauseMillis=200 最大停顿时间
  • -XX:G1NewSizePercent 新生代占比
  • -XX:G1MaxNewSizePercent 新生代最大占比
  • -XX:G1ReservePercent=10 保留的内存占比以避免to区的溢出
  • -XX:ParallelGCThreads=n 并行的线程数,会停止应用的
  • -XX:ConcGCThreads=n 并发线程数,和应用程序一起执行的,默认是1/4的
  • -XX:ParallelGCThreads=n

3.8、 样例

针对JDK1.6
-server -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70

参数解释:
-server VM有两种运行模式Server与Client,两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多;
-Xmx4g 最大堆大小;
-Xms4g 初始堆大小;
-Xmn256m 堆中年轻代大小;
-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;
XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4;
-Xss 每个线程的Stack大小;
-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用。如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样;
-XX:+UseConcMarkSweepGC 并发标记清除(CMS)收集器,CMS收集器也被称为短暂停顿并发收集器;
-XX:+CMSParallelRemarkEnabled 降低标记停顿;
-XX:+UseCMSCompactAtFullCollection: 使用并发收集器时,开启对年老代的压缩;
-XX:LargePageSizeInBytes 指定Java heap的分页页面大小;
-XX:+UseFastAccessorMethods 原始类型的快速优化;
-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义的初始化大小开始CMS收集;
-XX:CMSInitiatingOccupancyFraction 使用cms作为垃圾回收使用70%后开始CMS收集;

针对Tomcat

对于Tomcat,在tomcat的bin目录下的catalina.sh中设置jvm参数:
JAVA_OPTS=”-server -Xmx4g -Xms4g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70”

并行垃圾收集器:多个GC线程可以同时收集;
并发垃圾收集器:多个GC线程和应用线程可以同时工作

4、 代码优化建议

尽量重用对象,不要循环创建对象,比如for循环字符串拼接;
容器类初始化的时候建议指定长度;list、map -->扩容影响性能;
ArrayList随机访问快,添加删除慢,LinkedList添加删除快,随机访问慢;
集合遍历尽量减少重复计算集合size(),使用变量存储;
使用Entry遍历Map;for(Map.entry entry : map.entrySet()) {}
大数组复制采用System.arraycopy(); -->它是native方法
尽量使用基本类型而不使用包装类型;int a = 10; 拆箱使用了.valueOf();
不要手动调用System.gc();
及时释放对象的引用防止内存泄漏,user[i] = null;–>数组、map等要注意;
多用局部变量少用成员变量,减小变量的作用域;
减少同步锁的作用范围,synchronized;
使用ThreadLocal缓存线程不安全的对象;
尽量使用延迟加载;
减少使用反射;
多采用连接池、线程池、对象池 (commons-pool.jar)、缓存等;
及时释放连接资源,数据库连接、IO流、socket连接等;
日志输出生产环境注意级别,日志参数使用占位符而不是拼接;
logger.info(“user id =”+ id);
logger.info(“user id = {}”, id);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值