【java性能调优】jvm篇

🤓 序

  今天周六,天气不错,做点啥呢?那就回顾以下自己六年以来关于JVM调优的案例讲一下,内容及其简介,尽可能结合场景让你拿来就用…


🤡 调优目标


💛 系统分析

基于企业内部系统

企业内部系统大多提供给企业内部的人使用,往往请求量不大,他所服务的客户是企业内部的员工,这部分系统大多满足于内部员工正常的业务需求或公司的管理诉求,对于使用本系统的人来说,系统内部可能会集成一些类似于插件功能,来支撑公司内部的一些业务开展,使用本系统能够提高工作效率,安全稳定,这部分系统在调优层面几乎不涉及!

  • 系统接口处理业务的速度如何?
  • 系统运行一段时间是否宕机?
  • 系统接口处理业务是否存在卡死情况?

基于互联网项目

这部分系统如电商,知乎,微博,系统的客户是来自每天上网的人士,可以说是海量的用户,每天有大量的客户群体访问系统。如何承受住这些流量压力,提升客户的体验,对于数据的保护和数据准确方面做的怎么样,系统稳定性如何等多种指标来考量,这部分系统涉及性能调优的方方面面!

  • 系统接口处理业务的速度如何?
  • 系统运行一段时间是否宕机?
  • 系统接口处理业务是否存在卡死情况?
  • 系统能否承受海量用户压力?
  • 系统对于业务处理数据准确性法如何保证?

💛 指标分析

QPS
QPS Queries Per Second 是每秒查询率 ,是一台服务器每秒能够响应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准, 即每秒的响应请求数,也即是最大吞吐能力。
TPS
TPS Transactions Per Second 也就是事务数/秒。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
RT
响应时间:RT(Response-time)就是从客户端请求发起到服务器响应结果的时间。RT这个参数是系统最重要的指标之一,它的大小直接反应了当前系统的响应状态。基本和咱们用户体验息息相关,现在好一点监控系统一般都有三个RT,即平均、最大、最小。

举个例子,如果公司里有600人,每天9点-10点要拉屎,厕所提供服务,但是一个人访问厕所,需要10分钟。请问,为了能够不排队,不拉裤子,需要多少个厕所才能够满足,如果600人同时访问厕所,需要6000分钟,但是一共要60分钟解决完毕,6000/60=60,也就是说需要创建60个厕所才能保证拉屎不排队!

厕所的QPS=1/600秒
厕所的TPS=600秒/1个事务数
厕所的RT=600s

生活中,厕所就相当于一台服务器,我们极可能的提升他的能力,让他有能力服务更多的客户,同时让客户有更好的体验,本次调优我们从吞吐量入手,搭配系统安全稳定的干货,通过jmeter压测实战案例具体细分调优细节…

💥 调优实战

准备

软件名称下载地址
springBoot项目不需要下载,空的即可
jmeterdownload

sringBoot项目走idea那套默认安装流程,启动项目访问即可,jmeter解压完毕,通过jmeter.bat启动。

🤡 OOM

OOM(Out Of Memory),即内存溢出,其问题表示java虚拟机在运行过程中,所占用的内存超过限制的内存大小了,导致没有多余的内存继续运行

我们要弄清楚该问题,首先要先了解java程序运行时的内存布局,我们知道java程序是运行在JVM(java虚拟机)之上的。因此其运行时的内存布局也就是JVM的内存布局。

JVM的内存布局(运行时数据区域)一共分为5部分:

堆:用于存放程序运行时创建的对象或数组,是我们最常操作的内存区域。
栈:用于存放栈帧,每个方法都会创建自己的栈帧,栈帧中包括局部变量表、操作栈、动态链接、返回地址等信息,其中局部变量表里存放基本数据类型和堆中对象的引用
程序计数器:用来存放在一条指令所处的位置的,这里是不会发生内存溢出的,因此大家了解即可
本地方法栈:与栈的作用类似,只不过栈用于管理JVM方法的调用,而本地方法栈用于管理本地方法的调用,所谓本地方法就是底层的操作系统指令、C、C++方法等
方法区:每个线程共享的内存区域,用于存储已经被JVM加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据


内存溢出区域

堆内存溢出
(1)创建了一个超大对象,比较常见的是一个大数组,大集合
(2)对象引用没有释放,导致垃圾无法回收,产生内存泄漏,从而导致可用内存减少
(3)突然而来的高并发,导致流量飙升,资源占用迅速提升,服务器配置无法跟上实际使用
(4)重写finalize引发频繁GC,这个问题的典型案例是小米云的C++程序员重写finalize导致了线上OOM。在java里很少见

栈内存溢出
栈内存溢出是线程请求的栈深度大于虚拟机允许的深度了,所谓的栈深度就是方法嵌套调用的次数,所以说的直白点就是嵌套循环调用次数太多!
(1)是否有递归调用
(2)是否有大量循环或死循环

模拟堆溢出

  • -Xms20M 最小内存
    默认值为容器内存的1/64, 最小8MB,如果明确指定了Xmx并且小于容器内存1/64, Xms默认值为Xmx指定的值。
  • -Xmx20M 最大内存
    容器内存小于等于2G:默认值为容器内存的1/2,最小16MB, 最大512MB。
    容器内存大于2G:默认值为容器内存的1/4, 最大可到达32G。
  • -XX:+HeapDumpOnOutOfMemoryError
    堆溢出生成快照

-Xms和-Xmx参数值为啥很多公司设置的都一样?

JVM的动态内存策略不太适合服务使用,因为每次GC需要计算Heap是否需要伸缩,内存抖动需要向系统申请或释放内存,特别是在服务重启的预热阶段,内存抖动会比较频繁。另外,容器中如果有其他进程还在消费内存,JVM内存抖动时可能申请内存失败,导致OOM。因此建议服务模式下,将Xms设置Xmx一样的值。



MAT分析JVM快照

MAT堆快照分析工具


堆溢出详情



MAT还有其他多种玩法,大家可以自行探索。

** 模拟栈溢出**

死递归


🦚 吞吐量

测试吞吐量,先把jvm参数去掉,用jmeter进行压测!
在这里插入图片描述
jmet.bat打开测试程序
选择中文

添加线程和监听器



持续300秒 每秒300个并发

修改注册表,主要为了方便看jvm测试效果!
1、打开注册表:regedit
2、进入
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Services\TCPIP\Parameters
3、新建 DWORD值,name:TcpTimedWaitDe,value:10(十进制) –> 设置为10秒,默认是240秒 【如果有这一项则修改,没有则新建】
4、新建 DWORD值,name:MaxUserPort,value:65534(十进制) –> 设置系统当前端口可用范围为65534 【如果有这一项则修改,没有则新建】
5、重启系统使注册表新增配置生效




我们只设置了一个参数就使异常率变为0,同时吞吐量也上来了,什么参数呢?
-XX:NewRatio=2
新生代占1,老年代占2,目前我们的程序主要是压测这个接口,这个接口的垃圾回收一般是在新生代比较多,我们把新生代设置的大一些, 极可能避免对象进入老年代,同时避免了fullGC发生的频率,从测试结果上是一个质的飞跃!


调整了一个参数又恢复正常了
-XX:MaxTenuringThreshold=15
这个参数表示对象进入老年代的年龄,如果对象在新生代过多的情况,可以适当调节,从而避免fullGC的频率!
在这里插入图片描述
9000并发的时候,又出现了异常率
由于我使用的是JDK17 可以指定垃圾收集器,指定G1垃圾收集器,异常率又变为0了
-XX:+UseG1GC

在这里插入图片描述

药方

4核8G,JDK8参数参考,具体要以实际项目及调优结果进行设置:
Xms4096m
Xmx4096m
Xmn3072m
XX:MetaspaceSize=256m
XX:MaxMetaspaceSize=256mXX:+UseParNewGC
XX:+UseConcMarkSweepGCXX:CMSInitiatingOccupancyFraction=92XX:+UseCMSCompactAtFullCollectionXX:CMSFullGCsBeforeCompaction=0XX:+CMSParallellnitialMarkEnabledXX:+CMSScavengeBeforeRemarkXX:+ DisableExplicitGC
XX:+PrintGCDateStamps
XX:+PrintGCDetails
Xloggc:gc.log
XX:+HeapDumpOnOutOfMemorvError
XX:HeapDumpPath=/usr/local/dumdir

JDK17参数调优 8核16G
java -server -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/heapdump -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:“/app/logs/mes-gcc-%t.log” -Djava.rmi.server.hostname=192.168.18.244 -Djava.net.preferIPv4Stack=true -XX:+PrintStringDeduplicationStatistics -XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:InitiatingHeapOccupancyPercent=80 -Dcom.sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions -XX:G1HeapRegionSize=1M -XX:+G1SummarizeRSetStats -XX:+PrintReferenceGC -XX:+PrintAdaptiveSizePolicy -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -Xss5000K -Xmx8192M -Xms8192M -XX:NewSize=4G -XX:MaxNewSize=6G -XX:SurvivorRatio=4 -XX:NewRatio=4 -Dfile.encoding=UTF-8 -Xverify:none -jar “%BASE%\mes-lastest.jar” --spring.profiles.active=dev --spring.mvc.async.request-timeout=10000 --server.undertow.io-threads=12 --server.undertow.worker-threads=1024 --server.undertow.buffer-size=1024

🔔 总结

  对于程序的性能调优不是目的,目的是解放程序员,有更多的时间陪伴家人,做自己想做的事,调优的过程比较枯燥,但是有迹可循,目前已经有很多有优秀的工具可供参考,本博客只列举了部分,还有阿里的阿尔萨斯,对于java程序进行细粒度的监控,通过jmeter压测工具,一步一步找到程序的瓶颈,结合MAT软件的分析,找到系统的薄弱项,对阵下药,解决系统根本问题!

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农佩奇

打酒喝!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值