您现在的位置是:首页 > 科技前沿

「性能优化」 JVM调优-参数篇

智慧创新站 2025-06-01【科技前沿】129人已围观

简介一、jvm优化的必要性在本地开发环境中我们很少会遇到需要对jvm进行优化的需求,但是到了生产环境,我们可能将会碰到下面的问题1、应用夯住,日志不输出,程序没有反应原因有很多,内存溢出,tomcat假死都有可能出现这种情况。详细描述见视频2、服务器的CPU负载突然升高你的线程量不断爆发,就像bio模型...

一、jvm优化的必要性

在本地开发环境中我们很少会遇到需要对jvm进行优化的需求,但是到了生产环境,我们可能将会碰到下面的问题

1、应用夯住,日志不输出,程序没有反应

原因有很多,内存溢出,tomcat假死都有可能出现这种情况。

详细描述见视频

2、服务器的CPU负载突然升高

你的线程量不断爆发,就像bio模型,一请求,一线程的这种模式,线程不断创建,回收不及时,那cpu肯定会居高不下,这种问题你在开发环境根本不会出现,因为就你一个人在跑,没有高并发,也就不会出现线程暴增的情况。

详细描述见视频

3、在多线程应用下,如何分配线程的数量?

上线之后才会出现多线程的情况,在本地开发环境是不会有这个需求的,因为就你一个人在开发这个功能,就一个线程在跑,所以上线后会有这个需求,线程分配的数量会根据系统软硬件环境,业务并发量做综合考量。

详细描述见视频

4、系统频繁FULLGC,最后内存泄漏

这种问题通常在上线之后的一段时间内会出现,原因也有很多,比如说代码原因,造成对象回收不及时,最后存活对象在老年代越来越多,最终没有内存可分配,导致内存泄漏。

在本次课程中,我们将对jvm更深入的学习,我们不仅要让程序能跑起来,而且是可以跑的更快!可以分析解决在生产环境中所遇到的各种“棘手”的问题。

二、jvm的运行参数

在jvm中有很多的参数可以设置,这样可以让jvm在各种环境中都能够高效的运行。绝大部分的参数保持默认即可,这一节介绍比较常见的以及比较经常设置的一些参数。

1、标准参数

1)、-help,-version,-D参数

jvm的标准参数,一般都是很稳定的,在未来的JVM版本中不会改变,可以使用java-help检索出所有的标准参数。通过以下命令查看:

java-help


可以看到我们经常会用到的-sever,-version等参数。

实战1:查看JVM版本

java-version


jvm版本是1.8.0_202,而且是64位,server,混合模式。

实战2:通过-D设置系统属性参数

先写一段代码:

publicclassTestVM{publicstaticvoidmain(String[]args){Stringname=("name");if(name!=null){(name);}else{("ling");}}}

运行上面这段代码,通过-D带入一个参数name,根据name的值进行判断如果name不为null,则打印,如果为null则打印ling。

把上面的java代码复制进去[root@localhosttest]设置参数进行,设置系统属性:‐D名称=值[root@localhosttest]java-client-showversionTestVMjavaversion"1.8.0_202"Java(TM)SERuntimeEnvironment(_202-b08)JavaHotSpot(TM)64-BitServerVM(,mixedmode)ling

可以看出因为服务器是64位的,所以都是server启动!

2、-X参数(非标准参数)

jvm的-X参数是非标准参数,在不同版本的jvm中,参数可能会有所不同,可以通过java-X查看非标准参数。

[root@myshop02~]强制设置为解释模式[root@myshop02~]强制设置为编译模式java-showversion-XcompTestVMjavaversion"1.8.0_202"Java(TM)SERuntimeEnvironment(_202-b08)JavaHotSpot(TM)64-BitServerVM(,compiledmode)lingjava-showversionTestVMjavaversion"1.8.0_202"Java(TM)SERuntimeEnvironment(_202-b08)JavaHotSpot(TM)64-BitServerVM(,mixedmode)ling
3、-XX参数(使用率较高)

-XX参数也是非标准参数,主要用于jvm的调优和debug操作。

-XX参数的使用有2种方式,一种是boolean类型,一种是非boolean类型:

boolean类型格式:-XX:[±]如:-XX:+DisableExplicitGC表示禁用手动调用gc操作,也就是说调用()无效

非boolean类型格式:-XX:如:-XX:NewRatio=1表示新生代和老年代的比值

用法:

[root@myshop02~]java-Xms512m-Xmx2048mTestVMling
5、查看jvm的运行参数

有些时候我们需要查看jvm的运行参数,这个需求可能会存在2种情况:

第一,运行java命令时打印出运行参数;

运行java命令时打印参数,需要添加-XX:+PrintFlagsFinal参数即可。

如:

[root@myshop02~]java-XX:+PrintFlagsFinal-XX:+VerifySharedSpaces-versionboolC1ProfileVirtualCalls=true{C1product}boolC1UpdateMethodData=true{C1product}intxCICompilerCount:=2{product}boolCICompilerCountPerCPU=true{product}boolCITime=false{product}cd/usr/local/tomcat9/bin[root@myshop02bin]执行jinfo-flags3694查看信息[root@myshop02bin]jinfo-flagMaxHeapSize3694-XX:MaxHeapSize=1073741824
三、jvm内存模型

jvm的内存模型在1.7和1.8有较大的区别,虽然本套课程是以1.8为例进行讲解,但是我们也是需要对1.7的内存模型有所了解,所以接下里,我们将先学习1.7再学习1.8的内存模型。

1、的堆内存模型


Young年轻区(代):Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候,GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,仍然存活于Survivor的对象将被移动到Tenured区间。

Tenured年老区:Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

Perm永久区:Perm代主要保存class,method,filed对象,这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到:PermGenspace的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。

Virtual区:最大内存和初始内存的差值,就是Virtual区。

2、的堆内存模型


由上图可以看出,的内存模型是由2部分组成,年轻代+年老代。年轻代:Eden+2*Survivor年老代:OldGen在中变化最大的Perm区,用Metaspace(元数据空间)进行了替换。需要特别说明的是:Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间中,这也是与1.7的永久代最大的区别所在。

3、为什么要废弃1.7中的永久区?

官网给出了解释:

(sinceJRockitdoesnothaveapermanentgeneration)andareaccustomedtonotconfiguringthepermanentgeneration.移除永久代是为融合HotSpotJVM与JRockitVM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

现实使用中,由于永久代内存经常不够用或发生内存泄露,报出异常:PermGen。基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间。

4、通过jstat命令查看堆内存使用情况

jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。

命令的格式如下:

jstat[-命令选项][vmid][间隔时间/毫秒][查询次数]

查看class加载数统计[root@myshop02bin]说明:Loaded:加载class的数量Bytes:所占用空间大小Unloaded:未加载数量Bytes:未加载占用空间Time:时间

查看编译统计[root@myshop02bin]说明:S0C:第一个Survivor区的大小(KB)S1C:第二个Survivor区的大小(KB)S0U:第一个Survivor区的使用大小(KB)S1U:第二个Survivor区的使用大小(KB)EC:Eden区的大小(KB)EU:Eden区的使用大小(KB)OC:Old区大小(KB)OU:Old使用大小(KB)MC:方法区大小(KB)MU:方法区使用大小(KB)CCSC:压缩类空间大小(KB)CCSU:压缩类空间使用大小(KB)YGC:年轻代垃圾回收次数YGCT:年轻代垃圾回收消耗时间FGCT:老年代垃圾回收消耗时间GCT:垃圾回收消耗总时间

四、jmap的使用以及内存溢出分析

前面通过jstat可以对jvm堆的内存进行统计分析,而jmap可以获取到更加详细的内容。如:内存使用情况的汇总、对内存溢出的定位与分析。

1、查看内存使用情况
[root@myshop02bin]堆内存配置信息MinHeapFreeRatio=40MaxHeapFreeRatio=70MaxHeapSize=1073741824(1024.0MB)NewSize=1363144(1.2999954223632812MB)MaxNewSize=643825664(614.0MB)OldSize=5452592(5.1999969482421875MB)NewRatio=2SurvivorRatio=8MetaspaceSize=21807104(20.796875MB)CompressedClassSpaceSize=1073741824(1024.0MB)MaxMetaspaceSize=415MBG1HeapRegionSize=1048576(1.0MB)HeapUsage:G1堆情况regions=1024capacity=1073741824(1024.0MB)used=45869552(43.74461364746094MB)free=1027872272(980.2553863525391MB)4.271934926509857%usedG1YoungGeneration:查看所有对象,包括活跃以及非活跃的jmap‐histopid|morejmap-histo:live3694|morenumbytesclassname----------------------------------------------1:311302898400[C2:1402802944[B3:30895741480:16607531424$Node5:3728422344:4621406648:7931253792$Node8:4409240128[;9:1138197296[$Node;10:1979129448[I11:112107808[$Node;12:180086400:532585200:260855976[;15:129251680$Entry16:10050304[$Entry;17:154449408$Entry

用法:jmap‐dump:format=b,file=dumpFileNamepid用法:jhat‐portportfilejhat-port9998/root//root/:10:56CST2020Snapshotread,resolvingResolving269315objectsChasingreferences,expect53dots..Eliminatingduplicatereferences..

打开浏览器进行访问:


如果访问不了,需要防火墙开放9998端口。

[root@myshop02~];

在最后面有OQL查询功能。


点开之后可以执行OQL查询语句,如下,左侧是查询结果,字符串长度=100的都查询出来了

5、MAT工具对dump文件进行分析

1)、MAT工具介绍

MAT(MemoryAnalyzerTool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVAheap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速地计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

2)、下载安装

官网下载地址:

下载解压后,双击打开

3)、使用

打开一个dump文件

选择自动检测

可疑对象查找

查看可能存在内存泄露的分析

五、实战:内存溢出的定位与分析

内存溢出在实际的生产环境中经常会遇到,比如,不断地将数据写入到一个集合中,出现了死循环,读取超大的文件等等,都可能会造成内存溢出。

如果出现了内存溢出,首先我们需要定位到发生内存溢出的环节,并且进行分析,是正常还是非正常情况,如果是正常的需求,就应该考虑加大内存的设置,如果是非正常需求,那么就要对代码进行修改,修复这个bug,我们需要借助于jmap与MAT工具进行定位分析。

接下来,我们模拟内存溢出的场景。

1、模拟内存溢出

编写代码,向List集合中添加100万个字符串,每个字符串由1000个UUID组成。如果程序能够正常执行,最后打印ok(idea编辑器中需要设置内存溢出相关参数)

用法:jstackpid[root@myshop02~]32daemonprio=9os_prio=0tid=0x00007fed14008800nid=0x138cwaitingoncondition[0x0000000000000000]:RUNNABLE"Catalina-utility-2"在tomcat的bin目录下,修改,添加如下的参数CATALINA_OPTS="$CATALINA_=192.168.0.108-=9999-=9999-==false"‐:允许使用JMX远程管理‐=false:不进行身份认证,任何用户都可以连接#‐=false:不使用ssl

使用jvisualvm连接远程tomcat添加远程主机(右击远程,添加主机)

在一个主机下可能会有很多的jvm需要监控,所以在该主机上添加需要监控的jvm

添加成功,如下图所示

八、本章小结

很赞哦!(67)