当前位置:首页 > Java技术 > 性能调优之JVM调优(四)GC性能指标及调优实战

性能调优之JVM调优(四)GC性能指标及调优实战

2022年11月10日 11:40:30Java技术10

什么是jvm调优

调优简单说就是把整个系统进行优化:

根据需求进行JVM规划和预调优,比如为程序设置合适的内存、选择合适的垃圾回收器等
当程序出现慢或者卡顿现象时,优化JVM运行环境
解决JVM运行中出现的各种问题,比如OOM

GC性能指标

  • 吞吐量: 工作线程运行时间占比总运行时间之比
    用户程序执行时间/(用户程序执行时间+垃圾回收时间)

单位时间内,STW 的时间最短 (发生2次STW,0.2+0.2=0.4),垃圾回收时间占比最低,这样就称吞吐量高.
业务场景:比如科学计算就要求吞吐量优先。
垃圾回收器:PS+PO

  • 暂停时间: 在执行垃圾回收的时候, 工作线程被暂停的时间
    单位时间内,可能发生5次STW,但是单次的STW时间最短(0.1+0.1+0.1+0.1+0.1=0.5)
    业务场景:电商网站,CSDN,对外提供的API等这类服务响应时间要优先。
    垃圾回收器:CMS,G1
  • 内存占用: Java堆内存占用的大小
  • 收集频率: 垃圾收集器工作的频率
  • 收集效率: 一个对象诞生到死亡的时间
    前三条是GC的矛盾之处, 如果要提高工作线程的吞吐量, 就必须降低工作线程的暂停时间, 那么垃圾收集的时间就必然降低, 内存占用就会提高
    高吞吐量这会让应用程序的用户感觉只有应用程序线程在做“生产性”工作。 所以直觉上,吞吐量越高程序运行越快。
    低暂停时间是 从用户的角度来看不管是GC还是其他原因导致一个工作线程被挂起始终是不好的。
    这于这种矛盾的取决于应用程序的类型,有时候甚至短暂的200毫秒暂停都可能打断终端用户体验。 因此,具有低的最大暂停时间是非常重要的,特别是对于一个交互式应用程序。
    综上所述,在设计(或使用)GC算法时​​,我们必须确定我们的目标:一个GC算法​​只可能针对两个目标之一(即只专注于最大吞吐量和最小合理暂停时间),或尝试找到一个二者的折衷。

HotSpot虚拟机上的垃圾收集

现有的垃圾回收器的目标是: 在保证最大吞吐量优先的情况下,合理降低停顿时间
对于一个对象HotSpot抛弃了使用引用计数的方式判断一个普通对象的生死, 而是使用可达性分析算法来判断一个对象的生死
其核心思想就是: 通过一系列的GC Roots对象作为起点开始向下搜素, 搜索走过的的路程称之为引用链, 对于一个对象, 如果没有一个引用链与GCRoot相连, 那么它就是一个不可达的对象, 虚拟机就会证明此对象不可用
但是在可达性分析算法中不可达的对象,也并非"非死不可"的,这时候他们暂时处在"缓刑"阶段。要宣告一个对象的真正死亡,至少要经历两次标记过程 :
如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被JVM调用过,虚拟机会将这两种情况都视为"没有必要执行",此时的对象才是真正"死"的对象。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它(这里所说的执行指的是虚拟机会触发finalize()方法)。finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象在finalize()中成功拯救自己(只需要重新与引用链上的任何一个对象建立起关联关系即可),那在第二次标记时它将会被移除出"即将回收"的集合;如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。
但是对于类对象的删除的要求的非常严格的, 删除一个类对选哪个要满足下面三个条件

  • 该类的所有实例都已经回收
  • 该类的类加载器都已经删除
  • 该类的对应Class对象没有在任何地方引用, 并且这个类对象没有在任何地方通过反射访问过该类
    只有满足上述三个条件. 回收器才会考虑回收回收这个类对象
    而对于真正的回收算法. 有
  • 标记清除算法, 但是会产生大量的内存碎片
  • 标记复制算法, 需要较大的空间, 而且复制起来比较耗时, 但是对于分代回收思想的年轻代, 可以使用复制算法
  • 标记整理算法, 对于老年代来说很友好, 因为其没有较大的内存空间, 他的思想就是将有用对象进行整理,防止出现内存碎片, 所以在实际的老年代回收提出了标记-清整算法, 就是先进行标记清除, 害怕出现内存碎片, 就再进行一次标记整理算法
  • 分代收集算法, 对于这个算法而言就是把堆区分为年轻代和老年代, 年轻代存放一些刚生产的对象, 老年代放置一些大对象或者经年轻代没杀死的对象, 对于年轻代他们使用Minor GC, 该思想采用复制算法, 收集频率也高, 回收速度也快
    虚拟机在进行minorGC之前会判断老年代最大的可用连续空间是否大于新生代的所有对象总空间,如果大于的话,直接执行minorGC。
    对于老年代和永久代他们使用full GC , 这个的收集频率就比较慢, 一般是快满的时候才进行一次, 采用标记整理算法, 速度会比Minor GC慢10倍左右
    显示调用System.gc()方法也是直接调用Full GC

如何进行调优

1、熟悉业务场景,选择合适的垃圾回收器

如果是响应时间优先,选择CMS、G1、ZGC;
如果是吞吐量优先,就选择ParallelGC(PS+PO)
2、计算内存需求,给JVM设置合适的内存大小

新生代能容纳所有【并发量 * (请求-响应)】的数据
幸存区大到能保留【当前活跃对象+需要晋升对象】
年轻代应该占整个堆内存的25%到50%
晋升阈值配置得当,让长时间存活对象尽快晋升
3、选定CPU,越高越好

4、设定日志参数
-xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFile=5
-XX:GCLogFileSize=20M
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCCause
在生产环境中日志文件,日志名字按照系统时间产生,循环产生,日志个数5个,每个大小20M,这样整体大小100M,便于控制,方便查找问题。

5、进行压测监控JMeter

系统cpu经常100%,如何调优

1、cpu100%那么一定是有线程在占用系统资源,先找到是哪个进程cpu占用高(top)
2、找到该进程中的哪个线程cpu高(top -H -p )
3、导出该线程的堆栈,需要将线程id从十进制转为十六进制,因为java线程栈文件中的线程id是十六进制(jstack PID 或 jstack PID > pid.tdump 导出文件)
4、查找哪个方法(栈帧)消耗时间(jstack)
5、工作线程占比高还是垃圾回收线程占比高

系统内存飙高,如何查找问题

1、通过jmap命令导出堆文件( jmap -dump:live,format=b,file=dump.hprof PID)
2、使用jhat、MAT、jvisualvm工具对dump文件进行分析

如何处理大对象?

大对象对于JVM来说是个噩耗。如果对象过大,当前新生代的剩余空间装不下它,那么就需要使用分配担保机制,将当前新生代的对象都复制到老年代中,给大对象腾出空间。分配担保涉及到大量的复制,因此效率很低。

那么,如果将大对象直接放入老年代,虽然避免了分配担保过程,但该对象只有当Full GC时才能被回收,而Full GC的代价是高昂的。如果大对象过多时,老年代很快就装满了,这时就需要进行Full GC,如果Full GC频率过高,程序就会变得很卡。

因此,对于大对象,有如下几种处理方法:

  1. 在写程序的时候尽量避免大对象
    从源头降低大对象的出现,尽量选择空间利用率较高的数据结构存储。
  2. 尽量缩短大对象的有效时间
    对象用完后尽快让它失效,好让垃圾收集器尽快将他回收,避免因在新生代呆的时间过长而进入老年代。

作者:liuec1002
来源链接:https://blog.csdn.net/liuerchong/article/details/122341794

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/69272.html

标签: JVM调优
分享给朋友:

“性能调优之JVM调优(四)GC性能指标及调优实战” 的相关文章

OpenWrite技术自媒体界的JVM一次编辑、随处发布

OpenWrite技术自媒体界的JVM一次编辑、随处发布

原文 :https://mp.weixin.qq.com/s/KUtJ2dwhBRuJ2G_-PkQFEA 最懂你的科技自媒体管理平台 【实用小工具推荐】给科技或技术同学们推荐一款比较好用的工具,可以实现一稿多发,主流的技术渠道基本涵...

这样调优:让你的 IDEA 快到飞起来,效率真高!

这样调优:让你的 IDEA 快到飞起来,效率真高!

点击上方“Java基基”,选择“设为星标” 做积极的人,而不是积极废人! 源码精品专栏  原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析...

[JVM教程与调优] 为什么要学习JVM虚拟机?

[JVM教程与调优] 为什么要学习JVM虚拟机?

JVM在我们开发阶段不会用到,但是到了生产环境中,那么就会变得非常重要了。 为什么这么说呢? 一方面,因为我们的生产环境是比较复杂的。各种可能的问题都会出现,比如说:硬盘坏了、网络坏了、CPU利用率高了等问题层次不穷。 另外一方面,在我们生产环境出现问题,还不好进行定位。因为没...

Java String(JVM角度)

Java String(JVM角度)

基本特性 存储结构变更 jdk8及之前的jdk版本中,String的内存存储结构是char[]字符数组,但是在Jdk9及之后改成了byte[]字节数组。 原因是,堆空间中大部分的字符串内容都是latin字符,基本上...

JVM - 双亲委派

JVM - 双亲委派

# JVM - 双亲委派 JDK版本:1.8 # 1、双亲委派机制 Java虚拟机对于class文件采用的加载策略是按需加载。也就是当需要使用该类时才会将该类的.class文件加载到内存中生成Class对象。并且加载某...

JVM之-内存模型(转)

JVM之-内存模型(转)

JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创建,在JVM退出的时候销毁。而其他的数据依赖于每一个线程,在线程创建时创建,在线程退出时销毁。   程序计数器 程序计数器是一块较小的内存空间,可以看作是当前线程所...

二: Jvm内存模型

二: Jvm内存模型

为什么jvm要有内存模型   在  上一章节  我们清楚代码的运行流程之后,那么下面一段代码我们就可以知道: 1. main线程启动,main()方法的栈帧压入main线程的虚拟机栈2. web()方法的栈帧也压入main线程的虚拟机栈3. web()栈...

十、JVM常用启动参数

 一、JVM启动参数共分为三类:   1.其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;   2.其二是非标准参数(-X),指的是JVM底层的一些配置参数,这些参数在一般开发中默认即可,不需要任何配置。但是在生...

jvm内存模型&GC垃圾回收机制

jvm内存模型&GC垃圾回收机制

jvm内存模型 OutOfMemoryError 出现异常: java.lang.OutOfMemoryError: Java heap space提示对内存异常 Exception in thread "main" java.lang.OutOfMemoryE...

Tomcat的JVM启动参数配置

Tomcat的JVM启动参数配置

一、windows环境下 1、添加JVM参数 到Tomcat的bin目录下,打开文件catalina.bat,添加如下参数,然后保存。 set "JAVA_OPTS=-Xms512M -Xmx1024M" 2、检验是否生效 双...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。