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)等设置, 对提升系统性能也有一定的帮助。