ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JVM性能调优实战

2022-04-10 02:01:11  阅读:344  来源: 互联网

标签:实战 sun XX 调优 gc 内存 JVM GC jmxremote


1 环境准备

CentOS 7 64位(内存4G)
JDK1.8
Tomcat 8

1.1 优化Tomcat

对于tomcat的优化,主要是从2个方面入手,一是,tomcat自身的配置,另一个是tomcat所运行的jvm虚拟机的调优。

#对tomcat进行优化配置 
vi apache-tomcat-8.5.34/conf/server.xml 

#优化一:禁用AJP服务,一般是使用Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。
#将下面的配置注释掉
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

#优化二:设置线程池,并且调整最大并发线程数
#<!--将注释打开-->
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true"
maxQueueSize="500"/>
#<!--
#参数说明:
# maxThreads:最大并发数,默认设置 200,一般建议在 500 ~ 1000,根据硬件设施和业务来判断 
# minSpareThreads:Tomcat 初始化时创建的线程数,默认设置 25
# prestartminSpareThreads: 在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,
# 如果不等于 true,minSpareThreads 的值就没啥效果了
# maxQueueSize,最大的等待队列数,超过则拒绝请求
# -->
# <!--在Connector中设置executor属性指向上面的执行器-->
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
               
# 优化三:设置tomcat运行模式为nio2,tomcat的运行模式有3种:bio、nio、apr,
# 其中nio2是nio的升级版,在 tomcat8中才支持的,建议采用nio2模式。
    <Connector executor="tomcatThreadPool"  port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               connectionTimeout="20000"
               redirectPort="8443" />

1.2 修改GC参数

vi apache-tomcat-8.5.34/bin/catalina.sh 

将下面的参数增加在配置顶部

 #内存设置较小是为了更频繁的gc,方便观察效果,实际要比此设置的更大
 #使用时去掉换行
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx128m 
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps 
-XX:+PrintHeapAtGC -Xloggc:/var/log/gc.log -Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9999 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false"

配置说明

  • 年轻代使用:ParallelGC,老年代使用ParallelOldGC
  • 初始Java堆64m 最大128m
  • gc日志保存在 /var/log/logs/gc.log
  • JMX连接端口9999,不需要密码验证,不开启https
    参数详细说明可以阅读《JVM常见参数及命令》

1.3 项目部署

测试war包下载地址:https://github.com/dvomu/document/tree/main/blog_doc/jvm
下载后放到服务器目录:apache-tomcat-8.5.34/webapps/ROOT
手动解压并删除war包

jar -xf gc-test.war
rm -rf gc-test.war

gc-test.war项目实现的功能:
读取电影文件movies.dat,载入到内存 查询电影数据时,随机返回1w ~ 10W个数据。每次请求随机取前10条数据返回。

1.4 启动项目

访问:http://192.168.88.101/movie/query
可以获取到数据即部署成功

1.5 JMeter环境准备

下面我们通过jmeter进行压力测试,先测得在初始状态下的并发量等信息,然后我们在对jvm做调优处理,再与初始状态测得的数据进行比较,看调好了还是调坏了。
JMeter内存调整
首先需要对jmeter本身的参数调整,jmeter默认的的内存大小只有1g,如果并发数到达300以上时,将无法正常执行,会抛出内存溢出等异常,所以需要对内存大小做出调整。
下载JMeter:https://jmeter.apache.org/download_jmeter.cgi
本文使用的JMeter 5.4.3版本。
修改jmeter/bin/jmeter.sh(Windows 改 jmeter.bat)文件:

# 最大内存增加到4G
set HEAP=-Xms1g -Xmx4g -XX:MaxMetaspaceSize=512m

2 第一次测试(PS + PO)

2.1 配置情况

按照上面配置,目前配置情况是

  • ParallelGC + ParallelOldGC
  • 初始Java堆64m 最大128m
  • 并发量200线程执行100次,共20000次请求

修改JMeter线程信息
线程组数量

HTTP请求信息

2.2 开始压测

统计垃圾回收情况
压测中使用jstat查看垃圾回收情况

发现FGC次数太多且频繁触发FullGC
查看聚合报告
通过查看聚合报告结果

测试进行到一半的时候发现已经出现比较严重的异常了。

2.3 gc日志分析

通过我们刚配置的目录将服务器/var/log/gc.log下载下来。通过GCeasy分析,GCeasy是在线免费分析工具,也可以使用GCViewer离线分析,推荐使用GCeasy。
关键指标

解读

  • 吞吐量:18.285%
  • STW平均时间:96.6毫秒
  • STW运行最大时间:320毫秒

GC之后数据分析

GC 持续时间

GC回收内存情况

Young GC 回收情况

Meta Space情况
Meta Space基本没有太大变化

数据分布情况

Minor GC清理掉的垃圾对象合计17.72gb,说明产生的临时对象非常的多
Minor GC的执行间隔为1044ms,说明发生gc的行为是比较频繁的
Full GC发生了1284次,非常频繁
Full GC的平均持续时间为167ms,时间较长 GC的暂停次数为2328次,暂停次数将影响到服务的响应时间

2.4 测试结论

基于上面报告分析结果,我们可以发现:

  • 年轻代与老年代在高峰时,基本将可用空间都占满了,说明内存空间不足,需要调整内存大小。
  • 整个测试系统吞吐量并不高,最大停顿时间较长,平均停顿时间较长。
  • full gc的时间要远高于younggc的时间,在调优时应当尽量的减少full gc。
  • 从老年代的gc情况来看,gc之前与之后的差并不大,说明老年代的垃圾对象并不是很多。
  • Meta Space空间充足,基本没有变化,占用空间40m左右。
  • 大部分发生gc的原因都为分配失败,也就说内存不足导致

3 第二次测试(PS + PO + 加内存)

3.1 配置调整

在jvm调优中,调整内存大小是调优手段中最为基本的一种手段,但是需要注意的是,内存的调整并不是简单的加大内存,而是需要结合业务特性、gc类型等内容进行调整。
对于我们目前测试的应用而言,属于及时响应、低延迟的应用,这样的应用在jvm堆内存中,对象的存活时间较短,所以应该将年轻代的内存调大些。

 #设置堆内存为1024m 
 #设置年轻代大小为512m,默认是堆内存的1/3 
 #设置初始的Metaspace大小为64m
#使用时去掉换行
 JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms1024m -Xmx1024m 
 -XX:NewSize=512m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 
 -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/var/log/gc.log 
 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 
 -Dcom.sun.management.jmxremote.authenticate=false 
 -Dcom.sun.management.jmxremote.ssl=false"


重新Tomcat后重新压测

3.2 开始压测

测试线程数不变。
统计垃圾回收情况

FullGC次数明显有下降

查看聚合报告

系统吞吐量也有明显提升,系统异常率彻底没有了。

3.3 gc 日志分析


吞吐量与停顿时间都有明显的提升。


项目启动前有两次FullGC应该是之前堆中存储的数据,在系统运行到中途的时候发生过一次FullGC,并且内存释放效果非常明显。


GC持续时间大部分都在200毫秒内,包括FullGC。

Full GC总共发生了3次,Minor GC的次数明显下降。情况有了很大的改善。
在对象的统计中,可以看出对象的平均生成率:495.72m/s,平均的晋升率:6.71mb/s。

3.4 测试结论

总体来讲,通过调整内存大小,对于服务的性能有了显著的提升。 下面尝试下将垃圾回收器更换成G1,看下表现怎么样?

4 第三次测试(G1)

选择性能更优的垃圾收集器也是调优的手段之一,在jdk8中,使用率最高的当属G1收集器了,下面我们就尝试切换成G1收集器,来看下它的表现。

4.1 配置情况

#使用时去掉换行
JAVA_OPTS="-XX:+UseG1GC -Xmx1024m -XX:MetaspaceSize=64m 
-XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 
-XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/var/log/gc.log 
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false"
 

按照上面配置,目前配置情况是

  • 垃圾收集器G1
  • 内存分配1024m
  • 停顿时间100毫秒
  • 并发量200线程执行100次,共20000次请求

4.2 开始压测

统计垃圾回收情况

FullGC没有出现过。

查看聚合报告

这个结果要比同等内存大小的ParallelGC性能稍好一些,但是提升并不明显。

4.3 gc 日志分析


停顿时间有了几倍的提升。

GC清理后内存释放空间也比较明显。

大多数的Young GC时间在100毫秒,小部分在200毫秒左右,没有发生FullGC 的情况。

4.4 测试结论

更换G1垃圾收集器后,其性能有所提升,如果在大内存的情况下效果会更好。原因是:G1垃圾收集器适合大内存低延迟的场景,比如设置6G、8G内存的场景下保持低延迟。

5 第四次测试(ZGC)

下面我们将垃圾收集器换成ZGC,需要注意的时,jdk需要切换到jdk11版本。

5.1 配置情况

修改配置

 #使用时去掉换行
 JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx1024m
-XX:MetaspaceSize=64m -Xlog:gc*:/var/log/gc.log -Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false"

按照上面配置,目前配置情况是

  • jdk11
  • 垃圾收集器ZGC
  • 内存分配1024m
  • 并发量200线程执行100次,共20000次请求

5.2 开始测试

查看GC统计

ZGC中没有分区的概念,也没有youngGC FullGC。
查看聚合报告

吞吐量明显变小了,可能是因为ZGC适合更大的内存,在小内存上发挥不了它的优势。

5.3 gc 日志分析


从报告上看,吞吐量百分比、STW平均时间、STW运行最大时间确实有明显的提升。


垃圾回收时间都在10毫秒,这是数据和官方宣称的比较吻合。

每次GC清理释放的内存空间,大多数GC清理所释放的内存都不多。

GC次数和前面几个也差不多,没有次数上的差异。

5.4 测试结论

在ZGC方面的表现,无论是吞吐量还是停顿时间均有不俗的表现。 综合起来看的话,ZGC的表现还是很不错的,如果给其设置大内存,依然可以得到较短的停顿时间。

6 总结

对于JVM的调优,给出大家几条建议:

  • 生产环境的JVM一定要进行参数设定,不能全部默认上生产。
  • 对于参数的设定,不能拍脑袋,需要通过实际并发情况或压力测试得出结论。
  • 对于内存中对象临时存在居多的情况,将年轻代调大一些。如果是G1或ZGC,不需要设定。
  • 仔细分析GCeasy给出的报告,从中分析原因,找出问题。
  • 对于低延迟的应用建议使用G1或ZGC垃圾收集器。
  • 不要将焦点全部聚焦jvm参数上,影响性能的因素有很多,比如:操作系统、tomcat本身的参数等。

标签:实战,sun,XX,调优,gc,内存,JVM,GC,jmxremote
来源: https://www.cnblogs.com/dooor/p/jvmty.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有