惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 郭慕荣

Docker部署zookeeper总结 Mac中git ssh的配置(GitLab) Java 类加载机制 面试题(一) jvm常用的参数有哪些? 怎么配置? hashmap 和currenthashmap 的原理?详解一下 aop在项目中使用的场景?怎么使用? Java dubbo spring springboot中的spi机制 spring中常见的两种代理模式 Redis的zset 面试汇总 drools 规则引擎在线化配置 - 郭慕荣 springcloud中常用的注解详解 springcloud中网关gateway总结 spring是怎么解决循环依赖的? MySQL 死锁 怎么处理? 在写left join的时候 是大表在左侧 还是小表在左侧(二) 在写left join的时候 是大表在左侧 还是小表在左侧(一) nacos客户端(接口调用者)如何感知被调用服务下线? (二) nacos客户端(接口调用者)如何感知被调用服务下线?(一) 在MySQL中 redolog undolog binlog 写入的场景,顺序 - 郭慕荣
如何监控和调优JVM性能?
郭慕荣 · 2025-10-29 · via 博客园 - 郭慕荣

JVM 性能监控与调优是保障 Java 应用稳定运行的核心环节,目标是减少内存溢出(OOM)风险、降低 GC 停顿时间、提高资源利用率(CPU / 内存)。整个过程需遵循 “监控指标→定位瓶颈→调整参数→验证效果” 的闭环,具体步骤和工具如下:

监控是调优的前提,需通过工具实时或离线采集关键指标,识别性能瓶颈(如内存泄漏、GC 频繁、线程阻塞等)。

按使用场景分为命令行工具(轻量、适合服务器环境)、可视化工具(直观、适合问题分析)、APM 工具(全链路监控、适合生产环境)。

  • jps:查看 Java 进程 ID(基础工具,用于定位目标进程)

    jps -l  # 显示进程ID和主类全路径(如 12345 com.example.Application)
    
  • jstat:监控 JVM 内存和 GC 状态(最常用的实时监控工具)

    # 格式:jstat -<选项> <进程ID> <间隔时间(ms)> <次数>
    jstat -gc 12345 1000 10  

    关键指标(以-gc为例):

    • S0C/S1C:Survivor 区总容量;S0U/S1U:Survivor 区已使用容量
    • EC/EU:Eden 区总容量 / 已使用容量
    • OC/OU:老年代总容量 / 已使用容量
    • MC/MU:元空间总容量 / 已使用容量
    • YGC/YGCT:新生代 GC 次数 / 总耗时(秒)
    • FGC/FGCT:Full GC 次数 / 总耗时(秒)
    • GCT:GC 总耗时(秒)
  • jmap:生成堆快照(.hprof)或查看堆内存分布(用于分析内存泄漏、大对象)

    jmap -heap 12345  # 查看堆内存配置和使用情况(如各区域大小、GC收集器)
    jmap -dump:format=b,file=heap.hprof 12345  
  • jstack:查看线程栈信息(用于分析线程阻塞、死锁、CPU 过高)

    jstack 12345 > thread.log  # 导出线程栈到文件
    # 结合top命令定位高CPU线程:top -Hp 12345 找到高CPU线程ID(十进制),转为十六进制在thread.log中搜索
    

    关键信息:线程状态(RUNNABLE/BLOCKED/WAITING)、锁信息(如synchronized阻塞的锁对象)。

  • jinfo:查看或修改 JVM 参数(动态调整部分参数,如 GC 日志开关)

    jinfo -flags 12345  # 查看当前JVM所有参数(如-Xms、-XX:UseG1GC等)
    jinfo -sysprops 12345  
  • JConsole:JDK 自带的简易监控工具(jconsole命令启动),支持监控:

    • 内存(堆 / 非堆实时使用趋势)、线程(状态、栈信息)、GC(次数 / 耗时)、MBean(JVM 内部管理接口)。
  • VisualVM:功能更全面的可视化工具(需单独下载VisualVM),支持:

    • 生成 / 导入堆快照、线程快照,内置分析器(内存 / CPU 分析);
    • 插件扩展(如 GC 日志分析、Visual GC 插件可视化 GC 过程)。
  • MAT(Memory Analyzer Tool):专注堆快照分析(下载地址),用于定位内存泄漏:

    • 自动检测内存泄漏疑点(“Leak Suspects” 报告);
    • 分析对象引用链(“Dominator Tree” 查看大对象的引用关系);
    • 计算对象 retained size(被该对象直接 / 间接引用的总内存)。
  • GCEasy:在线 GC 日志分析工具(官网),上传 GC 日志后生成可视化报告:

    • GC 频率、停顿时间分布、内存增长趋势;
    • 自动诊断问题(如 “Full GC 过于频繁”“新生代过小”)。

适合分布式系统,结合业务指标监控 JVM 性能,如:

  • Prometheus + Grafana:通过jmx_exporter暴露 JVM 指标(如堆内存、GC 次数),Grafana 可视化监控面板,支持告警(如 GC 停顿超阈值)。
  • SkyWalking:全链路追踪工具,内置 JVM 监控模块(内存、GC、线程、CPU),可关联业务调用链定位性能瓶颈(如某接口调用导致 GC 频繁)。
  • Elastic Stack:通过Metricbeat采集 JVM 指标,Elasticsearch存储,Kibana可视化,适合大规模集群监控。
  • 内存指标:

    • 堆内存:Eden 区使用率(持续接近 100% 可能导致 YGC 频繁)、老年代增长率(过快可能导致提前 Full GC)、Survivor 区是否被充分利用(过小可能导致对象提前进入老年代)。
    • 元空间:是否持续增长(如频繁动态生成类可能导致元空间 OOM)。
    • 直接内存:NIO 应用需关注(如 Netty,默认与堆最大值一致,溢出会抛OutOfMemoryError: Direct buffer memory)。
  • GC 指标:

    • 频率:YGC 次数(如每秒 > 1 次可能过频)、Full GC 次数(正常应很少,如每小时 > 1 次需警惕)。
    • 耗时:YGC 单次耗时(如 > 100ms 影响响应)、Full GC 单次耗时(如 > 1s 可能导致应用卡顿)。
    • 停顿率:GC 总耗时 / 应用运行总时间(吞吐量优先应用需 < 5%,低延迟应用需 < 1%)。
  • 线程指标:

    • 线程总数:是否持续增长(可能存在线程泄漏,如线程池未关闭)。
    • 阻塞 / 等待线程数:BLOCKED线程过多(锁竞争激烈)、WAITING线程过多(如线程池队列满导致线程等待)。
    • 死锁:jstack 可检测到死锁线程(需立即处理,避免资源耗尽)。
  • CPU 与负载:

    • JVM 进程 CPU 占比:过高(如 > 80%)可能是代码效率低(如死循环、频繁 GC)。
    • 系统负载:与 CPU 核心数匹配(如 4 核 CPU,负载长期 > 4 说明系统繁忙)。

调优需基于监控数据定位瓶颈,而非盲目调整参数。核心步骤:明确目标→采集数据→分析瓶颈→调整参数→验证效果。

  • 吞吐量优先:批处理任务(如数据分析),优先保证单位时间内完成更多任务,允许偶尔长 GC 停顿(如 Parallel GC)。
  • 低延迟优先:Web 应用、实时服务,优先减少响应时间波动,GC 停顿需控制在毫秒级(如 G1、ZGC)。
  • 内存高效:资源受限环境(如容器),需平衡内存占用和性能(避免过度分配内存)。

现象:老年代内存持续增长,Full GC 后内存不下降,最终 OOM;jmap -histo:live 显示某类实例数异常多。排查:

  1. 生成堆快照(jmap -dump或 OOM 自动生成);
  2. 用 MAT 分析:查看 “Dominator Tree”,找到占用内存最大的对象,通过 “Path to GC Roots” 分析引用链(为何未被回收)。 调优方案:
  • 修复代码:释放无用引用(如静态集合未清理、缓存未设置过期时间);
  • 临时缓解:增大堆内存(-Xmx),但需结合代码修复(否则只是推迟 OOM)。

现象:jstat 显示 YGC 每秒多次,或 Full GC 每几分钟一次,GC 总耗时占比高(>10%)。排查:

  • YGC 频繁:通常是新生代过小,或对象创建速度快(如大量临时对象)。
  • Full GC 频繁:老年代增长快(如大对象直接进入老年代、Survivor 区不足导致对象提前晋升)。 调优方案:
  • 调整新生代大小:增大-Xmn(如从 1G 增至 2G),减少 YGC 次数;
  • 调整 Survivor 区比例:-XX:SurvivorRatio=6(Eden:S0:S1=6:1:1,增大 Survivor 区,减少对象提前进入老年代);
  • 控制大对象:设置-XX:PretenureSizeThreshold=1048576(1MB 以上大对象直接进入老年代,避免新生代频繁 GC);
  • 更换 GC 收集器:如从 Parallel GC 换为 G1,通过-XX:MaxGCPauseMillis=100控制停顿时间。

现象:单次 GC 停顿 > 1s(如 Full GC 耗时 5s),导致应用响应超时(如 Web 请求超时)。排查:

  • 老年代过大:Full GC 需扫描整个老年代,耗时随内存增大而增加;
  • GC 收集器不合适:如 Serial GC(单线程回收)在大堆场景下停顿长。 调优方案:
  • 拆分大堆:若堆内存 > 16GB,考虑用 G1(区域化回收,只处理部分区域)或 ZGC(并发回收,停顿 < 10ms);
  • 调整 G1 参数:-XX:G1HeapRegionSize=8m(增大区域大小,减少区域数量)、-XX:InitiatingHeapOccupancyPercent=40(提前触发混合回收,避免老年代占比过高);
  • 减少老年代对象:优化代码,避免创建长生命周期大对象(如缓存对象序列化存储)。

现象:应用响应慢,jstack 显示大量BLOCKED线程,或存在死锁(Found one Java-level deadlock)。排查:

  • 死锁:jstack 日志中搜索 “deadlock”,查看相互持有锁的线程和锁对象;
  • 锁竞争:BLOCKED线程等待的锁对象被少数线程长期持有(如synchronized修饰的慢方法)。 调优方案:
  • 解决死锁:调整锁获取顺序(如统一按对象哈希值顺序获取锁);
  • 减少锁竞争:
    • 缩小锁范围(只锁必要代码块,而非整个方法);
    • 用更高效的锁(如ReentrantLock替代synchronized,支持公平锁 / 尝试获取);
    • 无锁化设计(如用Atomic类、ConcurrentHashMap 替代同步容器)。

现象:元空间内存持续增长,jstat -gc显示MU接近MC,最终 OOM。原因:频繁动态生成类(如 CGLib 代理、反射生成类),且类未被卸载(类加载器未回收)。调优方案:

  • 增大元空间上限:-XX:MaxMetaspaceSize=512m(临时缓解);
  • 优化类生成:复用代理类(如 Spring AOP 设置proxyTargetClass=true减少动态类)、避免频繁创建类加载器。
  1. 不过早调优:先保证功能正确,性能问题明确后再调优(多数性能问题源于代码,而非 JVM 参数)。
  2. 基于数据调优:所有调整必须有监控数据支撑(如 “调大 - Xmn 后 YGC 次数从 5 次 / 秒降至 1 次 / 秒”),避免凭感觉改参数。
  3. 小步迭代:每次只调整 1-2 个参数,对比前后指标变化(如先调新生代大小,再调 GC 收集器)。
  4. 结合业务场景:Web 应用优先保证低延迟(用 G1/ZGC),批处理优先保证吞吐量(用 Parallel GC)。
  5. 长期监控:调优后需持续监控(如 24 小时),确认效果稳定(避免短期优化导致长期问题)。

JVM 性能监控的核心是通过工具(jstat、MAT、APM)采集内存、GC、线程指标;调优则需针对具体瓶颈(内存泄漏、GC 频繁、线程阻塞),结合业务目标调整参数或优化代码。关键是形成 “监控→分析→调整→验证” 的闭环,而非依赖经验主义。