Java学习篇之JVM 调优

Java学习篇之JVM 调优

  • [一、JVM 是什么?](#一、JVM 是什么?)
  • [二、JVM 官方参数建议](#二、JVM 官方参数建议)
  • 三、JVM调优的场景
  • 四、如何监控JVM
  • 五、JVM调优的流程步骤
    • [1. 明确优化目标](#1. 明确优化目标)
    • [2. 监控和分析](#2. 监控和分析)
    • [3. 确定调优参数](#3. 确定调优参数)
    • [4. 实施调优策略](#4. 实施调优策略)
    • [5. 持续观察和调整](#5. 持续观察和调整)
    • [6. 定期评估和优化](#6. 定期评估和优化)

一、JVM 是什么?

JVM,全称Java Virtual Machine(Java虚拟机),是一个能够运行Java字节码的抽象计算机。

JVM是Java跨平台特性的核心。Java源代码在编译成字节码后,可以在任何支持Java的平台上运行,只要该平台安装了相应的JVM。这是因为JVM将Java字节码转换成特定平台上的机器码来执行,从而实现了"一次编写,到处运行"的跨平台特性。

二、JVM 官方参数建议

JVM 经过这么多年的发展和验证,整体是非常健壮的。个人认为99%的情况下,基本用不到 JVM 调优。

通常来说,我们的 JVM 参数配置大多还是会遵循 JVM 官方的建议,例如:

1. 堆内存设置

参数 描述 官方建议/说明
-Xms 设置JVM的初始堆大小 可以设置为物理内存的1/64(但小于1GB);对于高并发应用,建议与-Xmx相同
-Xmx 设置JVM的最大堆大小 可以设置为物理内存的1/4(但小于1GB)
-Xmn 设置年轻代的初始和最大大小 可以配置为整个堆的3/8;或使用-XX:NewSize/-XX:MaxNewSize分别设置

2. 年轻代与老年代比例

参数 描述 官方建议/说明
-XX:NewRatio 设置年轻代与老年代的比例 默认值为2(即老年代是年轻代的2倍)
-XX:SurvivorRatio 设置Eden区与一个Survivor区的大小比例 默认值为8,表示Eden区与Survivor区的比例为8:1

3. 线程堆栈设置

参数 描述 官方建议/说明
-Xss 设置每个线程的堆栈大小 JDK5.0后默认1MB;小型应用可能128KB足够,大型应用建议256KB

4. 非堆内存设置

参数 描述 官方建议/说明
-XX:PermSize 设置持久代的初始大小(JDK8已弃用) JDK8前用于控制方法区内存大小
-XX:MaxPermSize 设置持久代的最大大小(JDK8已弃用) JDK8前用于控制方法区内存大小
-XX:MetaspaceSize 设置元空间的初始大小(JDK8及以后) 替代了PermSize,用于存放类的元数据
-XX:MaxMetaspaceSize 设置元空间的最大大小(JDK8及以后) 替代了MaxPermSize

5. 垃圾收集器设置

参数 描述 官方建议/说明
-XX:+UseSerialGC 使用串行垃圾收集器 默认GC方式,适用于小型应用和单处理器环境
-XX:+UseParNewGC 在年轻代中使用并行线程进行垃圾收集 可以与CMS GC一起使用
-XX:+UseParallelGC 在年轻代中使用并行垃圾收集器 适用于多处理器系统
-XX:+UseParallelOldGC 在老年代中使用并行垃圾收集器 与-XX:+UseParallelGC一起使用
-XX:+UseConcMarkSweepGC 使用CMS垃圾收集器 在GC运行时对应用程序影响较小

6. 辅助信息设置

参数 描述 官方建议/说明
-XX:+PrintGCDetails 打印垃圾收集的详细情况 用于调试和性能分析
-Xloggc:filename 将GC日志信息记录到指定文件 便于分析和监控JVM的垃圾回收行为

7. 其他重要参数

参数 描述 官方建议/说明
-XX:MinHeapFreeRatio 设置GC事件后允许的最小可用堆空间百分比 控制JVM何时扩展堆大小
-XX:MaxHeapFreeRatio 设置GC事件后允许的最大可用堆空间百分比 控制JVM何时收缩堆大小
-XX:MaxDirectMemorySize 设置Direct ByteBuffer分配的堆外内存的最大大小 当达到此大小时,将触发Full GC

请注意,这些建议是基于Oracle JDK的通用指南,并且可能会随着JVM版本的更新而有所变化。

在实际应用中,可能还需要根据具体的应用场景、硬件环境和性能需求进行进一步的调整和优化。

同时,也需要注意参数的兼容性和稳定性问题。

三、JVM调优的场景

主要就是出现以下情况时,就需要注意对JVM进行调优了

  1. 当应用程序运行缓慢或出现卡顿,或是偶尔响应慢
  2. 应用程序频繁出现内存溢出(OutOfMemoryError)或内存泄漏等问题,

四、如何监控JVM

JVM监控工具分为JDK自带工具和第三方工具两大类。

  • JDK自带工具(常用的):jconsole、visualvm
  • 第三方工具(常用的):JProfiler、YourKit

JDK自带工具常用命令:

  • jstack pid: 打印堆栈相关执行信息,可以用于死锁发现。
  • jinfo pid: 打印进程的启动详细参数、JVM正在使用的参数。
  • jstat -gc pid: 打印各个分代的内存使用情况。可以每个一段时间打印一次。
  • jvisualvm: windows图形化界面 ,不常用。上线之前内测可以使用
  • jmap: 如果内存特别大,jmap会导致线程的停止,所以线上不能使用
    线上时不能dump下来文件,因为文件可能很大(堆内存占了多少文件就有多大),dump时java程序会暂停,线上不能dump。
    jmap -histo pid | head -n 20 :查找有多少对象产生,及对象占用的内存,对象的名称。可用于内存泄露导致的OOM。

五、JVM调优的流程步骤

JVM调优流程步骤可以归纳为以下几点:

1. 明确优化目标

在开始调优之前,首先需要明确优化的目标。这包括响应时间、吞吐量、内存使用率等指标。明确目标后,可以更有针对性地进行调优。

2. 监控和分析

  1. 使用监控工具:利用Java VisualVM、JConsole、JProfiler等监控工具,分析CPU使用率、内存使用情况和垃圾回收频率等指标。

JDK自带工具常用命令:

  • jstack pid: 打印堆栈相关执行信息,可以用于死锁发现。
  • jinfo pid: 打印进程的启动详细参数、JVM正在使用的参数。
  • jstat -gc pid: 打印各个分代的内存使用情况。可以每个一段时间打印一次。
  • jvisualvm: windows图形化界面 ,不常用。上线之前内测可以使用
  • jmap: 如果内存特别大,jmap会导致线程的停止,所以线上不能使用
    线上时不能dump下来文件,因为文件可能很大(堆内存占了多少文件就有多大),dump时java程序会暂停,线上不能dump。
    jmap -histo pid | head -n 20 :查找有多少对象产生,及对象占用的内存,对象的名称。可用于内存泄露导致的OOM。
  1. 分析GC日志:通过GC日志,了解垃圾回收的频率、停顿时间以及内存使用情况,帮助定位性能瓶颈。

例如:

bash 复制代码
-Xloggc:/path/to/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps

这些选项将GC日志记录到指定的文件(/path/to/gc.log),并打印详细的GC信息和时间戳。

然后,我们可以分析GC日志以了解垃圾回收的频率、停顿时间和内存使用情况。例如,我们可以查>找Full GC的发生频率和每次Full GC的停顿时间。

3. 确定调优参数

根据监控和分析的结果,确定需要调整的JVM参数。常见的JVM参数包括:

  1. 堆内存设置:通过-Xms和-Xmx设置初始和最大堆内存大小。

例如:

bash 复制代码
-Xms512m -Xmx512m

这将初始堆内存设置为512m,最大堆内存设置为512m。两者最好一致

  1. 垃圾收集器选择:选择合适的垃圾收集器,如G1、CMS等,以优化内存管理。

例如:

选择G1垃圾收集器:以优化内存管理和减少停顿时间。

bash 复制代码
-XX:+UseG1GC

调整G1垃圾收集器的参数:以进一步优化性能。

bash 复制代码
-XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=35

这将G1垃圾收集器的目标停顿时间设置为200毫秒,并将触发并发GC的堆占用百分比设置为35%。

  1. 线程栈大小:通过-Xss设置每个线程的堆栈大小,以减少线程上下文切换的开销。

4. 实施调优策略

  1. 调整JVM参数:根据确定的调优参数,调整JVM的启动参数。

例如:如果我们在Linux系统上使用java命令启动应用程序,我们可以这样设置:

bash 复制代码
java -Xms512m -Xmx512m -Xloggc:/path/to/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=35 -jar myapp.jar
  1. 优化代码:减少不必要的对象创建、使用高效的数据结构、避免过度的同步等,都可以显著提升应用的性能。

5. 持续观察和调整

  1. 持续监控:在调整参数和优化代码后,使用监控工具持续观察应用的性能变化。
  2. 对比性能指标:通过对比调优前后的性能指标,确认是否达到了预期的优化目标。
  3. 重新评估和调整:如果没有达到目标,可能需要重新评估调优策略,进行进一步的调整。

6. 定期评估和优化

性能调优是一个持续的过程。随着应用的演变和用户负载的变化,可能需要定期重新评估和调整JVM参数。定期的性能测试和监控可以帮助及时发现潜在问题,确保系统的稳定性和高效性。

参考文章

【1】面试官:如何进行 JVM 调优(附真实案例)

【2】【JVM】JVM 实战调优指南赋案例(保姆篇)

【3】JVM调优实例记录

【4】【Java】JVM调优操作手册(实战篇)

【5】JVM性能优化实战手册:从监控到调优策略

相关推荐
向宇it几秒前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行3 分钟前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
星河梦瑾1 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富1 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想1 小时前
JMeter 使用详解
java·jmeter
言、雲1 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
java1234_小锋1 小时前
JVM对象分配内存如何保证线程安全?
jvm
Yvemil72 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。2 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea