JVM性能调优

一、前言

性能调优,顾名思义,就是对系统或软件的性能进行优化,以提高其运行效率和响应速度。在计算机科学中,性能调优通常涉及到硬件、操作系统、数据库、网络等多个方面。对于Java开发者来说,JVM(Java虚拟机)的性能调优是非常重要的一环,因为JVM的性能直接影响到Java程序的运行效率。

性能调优包含多个层次,比如:架构调优、代码调优、JVM调优、数据库调优、操作系统调优等。架构调优和代码调优是JVM调优的基础,其中架构调优是对系统影响最大的。

JVM性能调优_第1张图片

性能调优基本上按照以下步骤进行:明确优化目标、发现性能瓶颈、性能调优、通过监控及数据统计工具获得数据、确认是否达到目标。

二、性能定义

要查找和评估器性能瓶颈,首先要知道性能定义,对于jvm调优来说,我们需要知道以下三个定义属性,依作为评估基础:

吞吐量:重要指标之一,是指不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。

延迟:其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。

内存占用:垃圾收集器流畅运行所需要 的内存数量。

这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得,具体某一个属性或者两个属性的性能对应用来说比较重要,要基于应用的业务需求来确定。

三、JVM调优的时机

遇到以下情况,就需要考虑进行JVM性能调优:

  1. 程序运行缓慢,响应时间过长;

  2. 程序运行时占用大量内存;

  3. 程序在高并发环境下运行不稳定;

  4. Heap内存(老年代)持续上涨达到设置的最大内存值;

  5. Full GC 次数频繁;

  6. GC 停顿时间过长(超过1秒);

  7. 应用出现OutOfMemory 等内存异常;

  8. 应用中有使用本地缓存且占用大量内存空间;

  9. 系统吞吐量与响应性能不高或下降。

四、调优的基本原则

优先架构调优和代码调优,JVM优化是不得已的手段,大多数的Java应用不需要进行JVM优化。

在调优前,我们需要理解相应原理和掌握技能:

  • 理解JVM的工作原理和架构;

  • 掌握JVM的性能指标和监控工具;

  • 根据实际需求和场景选择合适的JVM参数;

  • 通过实验和数据分析来验证调优效果。

在调优过程中,应该谨记以下3个原则,以便完成垃圾收集的调优,从而达到应用程序的性能要求。

  1. MinorGC回收原则:每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率。

  2. GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。

  3. GC调优3选2原则: 在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。

五、JVM调优目标

JVM调优的主要目标是提高程序的运行效率和响应速度,减少资源消耗,提高系统的稳定性和可扩展性。具体来说,主要包括以下几个方面:

  1. 提高程序的运行速度;

  2. 减少内存消耗;

  3. 减少GC的频率和时间;

  4. 提高系统的吞吐量和并发能力。

吞吐量、延迟、内存占用三者类似CAP,构成了一个不可能三角,只能选择其中两个进行调优,不可三者兼得。

  • 延迟:GC低停顿和GC低频率;

  • 低内存占用;

  • 高吞吐量;

选择了其中两个,必然会会以牺牲另一个为代价。

六、调优工具

常用的JVM调优工具有:

  • JConsole:Java自带的图形化监控工具,用于对 JVM 中的内存、线程和类等进行监控,可以实时查看JVM的各种性能指标;

  • VisualVM:功能强大的多合一性能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等,可以监控多个Java进程,提供详细的性能分析报告;

  • JProfiler:专业的Java性能分析工具,可以深入分析Java代码的执行效率,找出性能瓶颈;

  • Java Mission Control:Oracle推出的新一代Java性能分析工具,可以实时监控和分析Java应用的性能。

  • GCViewer:垃圾回收日志文件分析工具,可以非常直观地分析出待调优点。Memory,分析Totalheap、Tenuredheap、Youngheap内存占用率及其他指标,理论上内存占用率越小越好;Pause,分析Gc pause、Fullgc pause、Total pause三个大项中各指标,理论上GC次数越少越好,GC时长越小越好;

JVM性能调优_第2张图片

七、JVM调优步骤

JVM调优的基本步骤如下:

  • 分析和定位问题:通过性能监控工具收集数据,分析GC日志及dump文件,分析程序的运行状况,找出性能瓶颈;

  • 选择合适的JVM参数:根据实际需求和场景,确定JVM调优量化目标,调整JVM的内存分配、垃圾回收策略等参数,依次调优内存、延迟、吞吐量等指标;

  • 进行实验和测试:修改JVM参数后,重新运行程序,观察性能变化,验证调优效果;

  • 分析和总结:对比观察调优前后的差异,不断的分析和调整,直到找到合适的JVM参数配置;

以上操作步骤中,某些步骤是需要多次不断迭代完成的。一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行。
JVM性能调优_第3张图片

八、JVM参数

JVM调优涉及的参数非常多,以下是一些常用的参数:

  1. -Xms:设置堆初始大小,s为strating;

  2. -Xmx:设置堆最大大小,x为max(一般来说-Xms和-Xmx的设置为相同大小,因为当heap自动扩容时,会发生内存抖动,影响程序的稳定性);

  3. -Xmn:设置新生代大小,n为new(-Xss:规定了每个线程虚拟机栈(堆栈)的大小);

  4. -XX:NewRatio:设置新生代与老年代的比例;

  5. -XX:SurvivorRatio:设置Eden区与Survivor区的比例;

  6. -XX:+UseParallelGC:选择并行垃圾回收器;

  7. -XX:+UseConcMarkSweepGC:选择CMS垃圾回收器;

  8. -XX:+UseG1GC:选择G1垃圾回收器;

  9. -XX:MaxGCPauseMillis:设置最大暂停时间目标;

  10. -XX:InitiatingHeapOccupancyPercent:设置触发Full GC的老年代占用率阈值。

JVM性能调优_第4张图片

运行时JIT编译器优化参数

JIT编译指的是字节码编译为本地代码(汇编)执行,只有热点代码才会编译为本地代码。解释器执行节约内存,反之可以使用编译执行来提升效率。

JVM性能调优_第5张图片

实用代码示例

示例1:设置和监控堆内存大小

JVM启动参数:

java -Xms512m -Xmx2g -jar YourApp.jar
  • -Xms512m:设置初始堆内存为512MB。

  • -Xmx2g:设置最大堆内存为2GB。

public class HeapSizeMonitoring {
    public static void main(String[] args) {
        // 获取运行时环境
        Runtime runtime = Runtime.getRuntime();

        // 打印JVM的初始内存和最大内存配置
        System.out.println("JVM初始内存大小:" + runtime.totalMemory() / (1024 * 1024) + " MB");
        System.out.println("JVM最大内存大小:" + runtime.maxMemory() / (1024 * 1024) + " MB");
    }
}

示例2:使用G1垃圾收集器并监控GC

JVM启动参数:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=300 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar YourApp.jar
  • -XX:+UseG1GC:使用G1垃圾收集器。

  • -XX:MaxGCPauseMillis=200:设置期望的最大GC暂停时间为300毫秒。

  • -XX:+PrintGCDetails 和 -XX:+PrintGCDateStamps:打印GC的详细信息和时间戳。

示例3:线程堆栈大小的设置与监控

JVM启动参数:

java -Xss512k -jar YourApp.jar
  • -Xss512k:设置每个线程的堆栈大小为512KB。

示例4:使用G1垃圾收集器并调优

JVM启动参数:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=300 -XX:InitiatingHeapOccupancyPercent=50 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar YourApp.jar
  • -XX:+UseG1GC:使用G1垃圾收集器。

  • -XX:MaxGCPauseMillis=300:尝试将GC的最大暂停时间控制在300毫秒以内。

  • -XX:InitiatingHeapOccupancyPercent=50:当堆占用率达到50%时开始GC。

  • -XX:+PrintGCDetails 和 -XX:+PrintGCDateStamps:打印详细的GC信息和时间戳。

示例5:监控垃圾收集信息

JVM启动参数:

java -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar YourApp.jar
  • -XX:+PrintGC:打印基本的GC信息。

  • -XX:+PrintGCDetails:打印GC的详细信息。

  • -XX:+PrintGCDateStamps:在GC日志中加入时间戳。

示例6:设置线程堆栈大小

JVM启动参数:

java -Xss1024k -jar YourApp.jar
  • -Xss1024k:设置每个线程的堆栈大小为1024KB。

示例7:监控类的加载和卸载

JVM启动参数:

java -XX:+TraceClassLoading -XX:+TraceClassUnloading -jar YourApp.jar
  • -XX:+TraceClassLoading:启用类加载跟踪。

  • -XX:+TraceClassUnloading:启用类卸载跟踪。

示例8:监控垃圾回收行为

JVM启动参数:

java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -jar YourApp.jar
  • -XX:+PrintGCDetails:打印垃圾回收的详细信息。

  • -XX:+PrintGCDateStamps:在垃圾回收日志中加入时间戳。

  • -Xloggc:gc.log:将垃圾回收日志记录到指定文件。

示例9:配置和使用字符串去重功能

JVM启动参数:

java -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics -jar YourApp.jar
  • -XX:+UseStringDeduplication:开启JVM的字符串去重功能。

  • -XX:+PrintStringDeduplicationStatistics:打印字符串去重的统计信息。

示例10:使用并行垃圾收集器

JVM启动参数:

java -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:+PrintGCDetails -jar YourApp.jar
  • -XX:+UseParallelGC:使用并行垃圾收集器。

  • -XX:GCTimeRatio=4:设置吞吐量目标,表示99%的时间用于应用程序,1%的时间用于垃圾收集。

  • -XX:+PrintGCDetails:打印垃圾收集的详细信息。

示例11:设置和监控ZGC垃圾收集器

JVM启动参数:

java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xlog:gc -jar YourApp.jar
  • -XX:+UnlockExperimentalVMOptions:解锁实验性VM选项。

  • -XX:+UseZGC:使用ZGC垃圾收集器。

  • -Xlog:gc:开启GC日志。

九、结语

合理的JVM调优可以显著提升应用的性能和稳定性。不过,请记得调优是一个持续的过程,需要根据应用的具体表现来不断调整和优化。

最后由于版本不断更新,JVM参数和具体说明,建议需要时请参考 oracle 官网的手册。

你可能感兴趣的:(其他,JVM,jvm)