深入理解JVM虚拟机:(五)GC调优与JVM实用参数

本文深入解析JVM参数,涵盖串行、并行、CMS及G1回收器的配置,提供实用调优案例,如降低停顿、吞吐量优先及使用大页策略,助力性能优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 GC相关参数总结

1.与串行回收器相关的参数

  • -XX:+UseSerialGC:在新生代和老年代使用串行回收器。

  • -XX:+SuivivorRatio:设置 eden 区大小和 survivor 区大小的比例。

  • -XX:+PretenureSizeThreshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。

  • -XX:MaxTenuringThreshold:设置对象进入老年代的年龄的最大值。每一次 Minor GC 后,对象年龄就加 1。任何大于这个年龄的对象,一定会进入老年代。

2.与并行 GC 相关的参数

  • -XX:+UseParNewGC:在新生代使用并行回收收集器。

  • -XX:+UseParallelOldGC:老年代使用并行回收收集器。

  • -XX:ParallelGCThreads:设置用于垃圾回收的线程数。通常情况下可以和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的。

  • -XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于 0 的整数。收集器在工作时,会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。

  • -XX:GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。

  • -XX:+UseAdaptiveSizePolicy:打开自适应 GC 策略。在这种模式下,新生代的大小,eden 和 survivor 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。

3.与 CMS 回收器相关的参数

  • -XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用 CMS+串行收集器。

  • -XX:+ParallelCMSThreads:设定 CMS 的线程数量。

  • -XX:+CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发,默认为 68%。

  • -XX:+UseFullGCsBeforeCompaction:设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。

  • -XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收。

  • -XX:+CMSParallelRemarkEndable:启用并行重标记。

  • -XX:CMSInitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。

  • -XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才进行 CMS 回收。

  • -XX:+CMSIncrementalMode:使用增量模式,比较适合单 CPU。

4.与 G1 回收器相关的参数

  • -XX:+UseG1GC:使用 G1 回收器。

  • -XX:+UnlockExperimentalVMOptions:允许使用实验性参数。

  • -XX:+MaxGCPauseMills:设置最大垃圾收集停顿时间。

  • -XX:+GCPauseIntervalMills:设置停顿间隔时间。

5.其他参数

  • -XX:+DisableExplicitGC: 禁用显示 GC。

2.常用调优案例和方法

在实际调优过程中,需要根据具体情况进行权衡和折中。

2.1 将新对象预留在新生代

由于 Full GC 的成本远远高于 Minor GC,因此某些情况下需要尽可能将对象分配在年轻代。因此,在 JVM 参数调优时可以为应用程序分配一个合理的年轻代空间,以最大限度避免新对象直接进入年老代的情况发生。

合理设置一个年轻代的空间大小。-Xmn 调整这个参数,最好设置成堆内存的3/8,例如最大-Xmx5G,那么 -Xmn应该设置成3/8*2大约在2G左右

设置合理的survivor区并提高survivor区的使用率。 第一种是通过参数-XX:TargetSurvivorRatio提高from区的利用率;第二种方法通过-XX:SurvivorRatio,设置一个更大的from区。

2.2 大对象进入老年代

因为大对象出现在年轻代很可能扰乱年轻代 GC,并破坏年轻代原有的对象结构。因为尝试在年轻代分配大对象,很可能导致空间不足,为了有足够的空间容纳大对象,JVM 不得不将年轻代中的年轻对象挪到年老代。因为大对象占用空间多,所以可能需要移动大量小的年轻对象进入年老代,这对 GC 相当不利。基于以上原因,可以将大对象直接分配到年老代,保持年轻代对象结构的完整性,这样可以提高 GC 的效率。

可以使用-XX:PetenureSizeThreshold 设置大对象直接进入老年代的阈值。

举例:-XX:PetenureSizeThreshold=1000000 当对象大小超过这个值时,将直接在老年代分配。

2.3 设置对象进入老年代的年龄

对象在新生代经过一次GC依然存活,则年龄+1,当年龄达到阀值,就移入老年代。 阀值的最大值通过参数: -XX:MaxTenuringThreshold 来设置,它默认是15。 在实际虚拟机运行过程中,并不是按照这个年龄阀值来判断,而是依据内存使用情况来判断,但这个年龄阀值是最大值,也就说到达这个年龄的对象一定会被移到老年代。

举例:-XX:MaxTenuringThreshold=1 即所有经过一次GC的对象都可以直接进入老年代。

2.4 稳定与震荡的堆大小

当 -Xms与 -Xmx设置大小一样,是一个稳定的堆,这样做的好处是,减少GC的次数。

当 -Xms与 -Xmx设置大小不一样,是一个不稳定的堆,它会增加GC的次数,但是它在系统不需要使用大内存时,压缩堆空间,使得GC应对一个较小的堆,可以加快单次GC的次数。

可以通过两个参数设置用语压缩和扩展堆空间:

  • -XX:MinHeapFreeRatio: 设置堆的最小空闲比例,默认是40,当堆空间的空闲空间小于这个数值时,jvm会自动扩展空间。

  • -XX:MaxHeapFreeRatio: 设置堆的最大空闲比例,默认是70,当堆空间的空闲空间大于这个数值时,jvm会自动压缩空间。

2.5 吞吐量优先案例

吞吐量优先的方案将会尽可能减少系统执行垃圾回收的总时间,故可以考虑关注系统吞吐量的并行回收收集器。在拥有4GB内存和32核CPU的计算机上,进行吞吐量的优化,可以使用参数:

java –Xmx3800m –Xms3800m –Xmn2G –Xss128k –XX:+UseParallelGC 
   –XX:ParallelGCThreads=20 –XX:+UseParallelOldGC
  • –Xmx380m –Xms3800m:设置 Java 堆的最大值和初始值。一般情况下,为了避免堆内存的频繁震荡,导致系统性能下降,我们的做法是设置最大堆等于最小堆。假设这里把最小堆减少为最大堆的一半,即 1900m,那么 JVM 会尽可能在 1900MB 堆空间中运行,如果这样,发生 GC 的可能性就会比较高;

  • -Xss128k:减少线程栈的大小,这样可以使剩余的系统内存支持更多的线程;

  • -Xmn2g:设置年轻代区域大小为 2GB;

  • –XX:+UseParallelGC:年轻代使用并行垃圾回收收集器。这是一个关注吞吐量的收集器,可以尽可能地减少 GC 时间。

  • –XX:ParallelGCThreads:设置用于垃圾回收的线程数,通常情况下,可以设置和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的;

  • –XX:+UseParallelOldGC:设置年老代使用并行回收收集器。

2.6 使用大页案例

在 Solaris 系统中,JVM 可以支持 Large Page Size 的使用。使用大的内存分页可以增强 CPU 的内存寻址能力,从而提升系统的性能。

java –Xmx2506m –Xms2506m –Xmn1536m –Xss128k -XX:++UseParallelGC
 –XX:ParallelGCThreads=20 –XX:+UseParallelOldGC –XX:+LargePageSizeInBytes=256m

–XX:+LargePageSizeInBytes:设置大页的大小。

2.7 降低停顿案例

为降低应用软件的垃圾回收时的停顿,首先考虑的是使用关注系统停顿的 CMS 回收器,其次,为了减少 Full GC 次数,应尽可能将对象预留在年轻代,因为年轻代 Minor GC 的成本远远小于年老代的 Full GC。

java –Xmx3550m –Xms3550m –Xmn2g –Xss128k –XX:ParallelGCThreads=20
 –XX:+UseConcMarkSweepGC –XX:+UseParNewGC –XX:+SurvivorRatio=8 –XX:TargetSurvivorRatio=90
 –XX:MaxTenuringThreshold=31
  • –XX:ParallelGCThreads=20:设置 20 个线程进行垃圾回收;

  • –XX:+UseParNewGC:新生代使用并行回收器;

  • –XX:+UseConcMarkSweepGC:老年代使用 CMS 收集器降低停顿;

  • –XX:+SurvivorRatio:设置 Eden 区和 Survivor 区的比例为 8:1。稍大的 Survivor 空间可以提高在新生代回收生命周期较短的对象的可能性(如果 Survivor 不够大,一些短命的对象可能直接进入老年代,这对系统来说是不利的)。

  • –XX:TargetSurvivorRatio:设置 Survivor 区的可使用率。这里设置为 90%,则允许 90%的 Survivor 空间被使用。默认值是 50%。故该设置提高了 Survivor 区的使用率。当存放的对象超过这个百分比,则对象会向老年代压缩。因此,这个选项更有助于将对象留在新生代。

  • –XX:MaxTenuringThreshold:设置年轻对象晋升到老年代的年龄。默认值是 15 次,即对象经过 15 次 Minor GC 依然存活,则进入老年代。这里设置为 31,即尽可能地让对象保存在新生代。

3.实用JVM参数

3.1 JIT编译参数

JVM的JIT(Just-In-Time)编译器,可以在运行时将字节码编译成本地代码,从而提高函数的执行效率。-XX:CompileThreshold 为 JIT编译的阈值, 当函数的调用次数超过 -XX:CompileThreshold 时,JIT就将字节码编译成本地机器码。 在Client 模式下, XX:CompileThreshold 的取值为1500;在Server 模式下, 取值是10000。JIT编译完成后, JVM便会使用本地代码代替原来的字节码解释执行。

设置参数示例
JIT编译阈值-XX:CompileThreshold-XX:CompileThreshold=1500
打印耗时-XX:CITime-XX:CITime
打印编译信息-XX:PrintCompilation-XX:PrintCompilation

3.2 堆快照(堆Dump)

获得程序的堆快照文件有很多方法, 比较常用的取得堆快照文件的方法是使用-XX:+HeapDumpOnOutOfMemoryError 参数在程序发生OOM时,导出应用程序的当前堆快照。

通过参数 -XX:heapDumpPath 可以指定堆快照的保存位置。

-Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:heapDumpPath=C:\m.hprof

3.3 错误处理

当系统发生OOM错误时,虚拟机在错误发生时运行一段第三方脚本, 比如, 当OOM发生时,重置系统

-XX:OnOutOfMemoryError=c:\reset.bat

3.4 取得GC信息

JVM虚拟机提供了许多参数帮助开发人员获取GC信息。

获取一段简要的GC信息,可以使用 -verbose:gc 或者 -XX:+PrintGC。它们的输出如下:

[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]

这段输出,显示了GC前的堆栈情况以及GC后的堆栈大小和堆栈的总大小。

如果要获得更加详细的信息, 可以使用 -XX:+PrintGCDetails。示例输出:

[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

它不仅包含了GC的总体情况,还分别给出了新生代、老年代以及永久区各自的GC信息,以及GC的消耗时间。

如果想要在GC发生的时刻打印GC发生的时间,则可以追加使用-XX:+PrintGCTimeStamps选项。因此,可以知道GC的频率和间隔。打印输出如下:

11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

如果需要查看新生对象晋升老年代的实际阈值, 可以使用参数 -XX:+PrintTenuringDistribution 。

如果需要在GC时打印详细的堆信息,则可以打开 -XX:+PrintHeapAtGC 开关。

如果需要查看GC与实际程序相互执行的耗时, 可以使用 -XX:+PrintGCApplicationtStoppedTime 和 -XX:+PrintGCApplicationConcurrentTime参数。它们将分别显示应用程序在GC发生时的停顿时间和应用程序在GC停顿期间的执行时间。它们的输出如下:

Total time for which application threads were stopped: 0.0468229 seconds
Application time: 0.5291524 seconds

为了能将以上的输出信息保存到文件,可以使用 -Xloggc 参数指定GC日志的输出位置。 如 -Xloggc:C:\gc.log。

3.5 类和对象跟踪

JVM 还提供了一组参数用于获取系统运行时的加载、卸载类的信息。

-XX:+TraceClassLoading 参数用于跟踪类加载情况,-XX:+TraceClassUnloading 用于跟踪类卸载情况。如果需要同时跟踪类的加载和卸载信息,可以同时打开这两个开关,也可以使用 -verbose:class 参数。

除了类的跟踪, JVM 还提供了 -XX:+PrintClassHistogram 开关用于打印运行时实例的信息。 当此开关被打开时,  当Ctrl+Break 被按下, 会输出系统内类的统计信息。

...
 4: 990 23760 java.lang.String
...

从左到右,依次是 序号、实例数量、总大小和类名等信息。

3.6 控制GC

-XX:+DisableExplicitGC 选项用于禁止显式的GC操作, 即禁止在程序中使用System.gc() 触发Full GC。

-Xnoclassgc 参数用于禁止系统进行类的回收, 即系统不会卸载任何类,进而提升GC的性能。

-Xincgc 参数,一旦启用这个参数,系统便会进行增量式的 GC,增量式的GC使用特定算法让GC线程和应用程序线程交叉执行,从而减小应用程序因GC而产生的停顿时间。

3.7 选择类校验器

为确保class文件的正确和安全,JVM需要通过类校验器对class文件进行验证。目前,JVM中有两套校验器。

在JDK1.6中默认开启新的类校验器,加速类的记载, 可以使用 -XX:-UseSplitVerifier 参数指定使用旧的类校验器(注意是关闭选项)。如果新的校验器校验失败,可以使用老的校验器再次校验。可以使用开关 -XX:-FailOverToOldVerifier关闭再次校验的功能。

3.8 Solaris下线程控制

在solaris下,JVM提供了几个用于线程控制的开关:

  • -XX:+UseBoundTreads: 绑定所有用户线程到内核线程, 减少线程进入饥饿状态的次数 。
  • -XX:+UserLWPSynchronization: 使用内核线程替换线程同步 。
  • -XX:+UserVMInterruptibleIO: 允许运行时中断线程。

3.9 使用大页

对同样大小的内存空间, 使用大页后, 内存分页的表项就会减少, 从而可以提升CPU从虚拟内存地址映射到物理内存地址的能力。 在支持大页的操作系统中,使用JVM参数让虚拟机使用大页,从而提升系统性能。

  • -XX:+UserlargePages: 启用大页。

  • -XX:LargePageSizeInBytes: 指定大页的大小。

3.10 压缩指针

在64位虚拟机上, 应用程序所占内存的大小要远远超出其32位版本(约1.5 倍左右)。这是因为64位系统拥有更宽的寻址空间, 与32位系统相比,指针对象的长度进行了翻倍。为了解决这个问题,64位的JVM虚拟机可以使用 -XX:+UseCompressedOops 参数打开指针压缩,从一定程度上减少内存的消耗,可以对以下指针进行压缩:

  • Class的属性指针(静态成员变量)
  • 对象的属性指针
  • 普通对象数组的每个元素指针

虽然压缩指针可以节省内存,但是压缩和解压指针也会对JVM造成一定的性能损失。

4.实战JVM调优

4.1 Tomcat启动加速

使用 startup.bat 启动Tomcat 服务器时,start.bat  调用了bin 目录下的calalina.bat 文件。 如果需要配置 Tomcat的JVM参数,可以将参数写入 catalina.bat 中。打开 catalina.bat,可以看到:

这段说明显示,配置环境变量CATALINA_OPTS或者JAVA_OPTS都可以设置Tomcat的JVM优化参数。根据说明建议,类似堆大小、GC日志和 JMX 端口等推荐配置在 CATALIN_OPTS 中。

获取GC信息可以加入:

set CATALINA_OPTS=-Xloggc:gc.log -XX:+PrintGCDetails

为了减少Minor GC的次数, 增大新生代:

set CATALINA_OPTS=%CATALINA_OPTS% -Xmx32M -Xms32M

禁用显示GC:

set CATALINA_OPTS=%CATALINA_OPTS% -XX:+DisableExplicitGC

在堆内存不变的前提下,为了能进一步减少Minor GC的次数,可以扩大新生代的大小:

set CATALINA_OPTS=%CATALINA_OPTS% -XX:NewRation=2

为了加快Minor GC的速度,在多核计算机上可以考虑使用新生代并行回收收集器,加快Minor GC 的速度:

set CATALINA_OPTS=%CATALINA_OPTS% -XX:+UseParallelGC

由于JVM虚拟机在加载类时,处于完全考虑,会对Class进行校验和认证,如果类文件是可信任的, 为了加快程序的运行速度,也可以考虑禁用这些效应:

set CATALINA_OPTS=%CATALINA_OPTS% -Xverify:none

4.2 JMeter介绍和使用

JMeter是Apache 下基于Java 的一款性能测试和压力测试工具。它基于Java 开发,可对HTTP 服务器和FTP服务器,甚至是数据库进行压力测试。

下载地址:http://jmeter.apache.org/download_jmeter.cgi

中文教程:https://www.yiibai.com/jmeter/

1)如何切换中文界面?

编辑/bin/jmeter.properties文件,

找到被注释的#language那一行,更改为 language=zh_CN

2)入门HTTP测试

使用版本:5.0 ,环境:windows

第一步:新建线程组

第二步:配置线程数10,每条线程循环200次。

第三步:配置取样器,这里是HTTP请求。

第四步:配置HTTP请求参数,服务器IP,端口号,路径,HTTP参数等。

第五步:生成测试报告。JMeter提供图形、表格等多种形式的报告,报告有各项参数,包括平均响应时间、错误数和吞吐量。这里是生成聚合报告。

第六步:配置完成后,单机顶部绿色的三角图形,启动,即可进行测试。测试完成后,查看吞吐量那一栏(Throughput)。

3)调优过程示例

为了减少GC次数, 可以使用合理的堆大小和永久区大小。这里将堆大小设置为512MB, 永久区使用32MB, 同时, 禁用显示GC, 并去掉类校验。参数如下:

set CATALINA_OPTS=%CATALINA_OPTS% "-Xmx512M"
set CATALINA_OPTS=%CATALINA_OPTS% "-Xms512M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:PermSize=32M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:MaxPermSize=32M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:+DisableExplicitGC"
set CATALINA_OPTS=%CATALINA_OPTS% "-Xverify:none"

为了进一步提高系统的吞吐量, 可以尝试使用并行回收收集器代替串行收集器。

set CATALINA_OPTS=%CATALINA_OPTS% "-Xmx512M"
set CATALINA_OPTS=%CATALINA_OPTS% "-Xms512M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:PermSize=32M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:MaxPermSize=32M"
set CATALINA_OPTS=%CATALINA_OPTS% "-XX:+DisableExplicitGC"
set CATALINA_OPTS=%CATALINA_OPTS% "-Xverify:none"
set CATALINA_OPTS=%CATALINA_OPTS% -XX:+UseParallelGC
set CATALINA_OPTS=%CATALINA_OPTS% -XX:+UseParallelOldGC
set CATALINA_OPTS=%CATALINA_OPTS% -XX:ParallelGCThreads=8

总结一下 JVM调优的主要过程有: 确定堆内存大小(-Xmx, -Xms)、合理分配新生代和老生代(-XX:NewRation, -Xmn, -XX:SurvivorRatio)、确定永久区大小: -XX:Permsize, -XX:MaxPermSize、选择垃圾收集器、对垃圾收集器进行合理的设置,除此之外,禁用显示GC(-XX:+DisableExplicitGC), 禁用类元数据回收(-Xnoclassgc), 禁用类验证(-Xverfy:none)等设置, 对提升系统性能也有一定的帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值