当前位置: 首页 > news >正文

JVM 调优

JVM 调优

  • 调优核心步骤
    • 1. 明确目标
    • 2. 监控与诊断(先定位瓶颈!)
    • 3. 关键指标解读
  • 核心调优参数详解
    • 1. 堆内存设置
    • 2.选择合适的 GC 算法(GC收集器)(JDK 17+ 推荐 G1)
    • 3. 优化分代比例(非G1时)
    • 4. 触发 Full GC 的关键点
  • 高级调优技巧
    • 1. 避免大对象直接进老年代
    • 2. G1 优化建议
    • 3. 减少 STW 停顿
    • 4. 容器环境(Docker/K8s)
  • 常见问题与解决
  • 调优原则

JVM 调优是 Java 性能优化的核心环节,目的是 减少停顿时间(STW)、提高吞吐量、降低内存占用,使应用运行更稳定高效。调优没有万能公式,需结合具体应用、硬件、GC 算法等进行。以下是系统化的调优指南:

调优核心步骤

1. 明确目标

问题可能出现的场景
1.低延迟(如 Web 服务 <100ms GC 停顿)
2.高吞吐量(如批处理,最大化 CPU 利用率)
3.小内存占用(容器环境)
4.避免 OOM(内存泄漏排查优先)

先确定是哪个场景的问题

2. 监控与诊断(先定位瓶颈!)

JVM 内置工具
jps(进程号)→ jstat -gc <pid> 1000(每秒 GC 状态)
jmap -heap <pid>(堆概览),jmap -histo:live <pid>(对象直方图)
jstack <pid>(线程栈,查死锁/阻塞)
可视化工具
JConsole、VisualVM、Eclipse MAT(内存分析)
专业 Profiler
Arthas(实时诊断)、Async Profiler(低开销)、JMC(Flight Recorder)

3. 关键指标解读

GC 日志(必备!启动参数加 -Xlog:gc*,gc+heap=debug:file=gc.log):
- Young GC 频率/耗时
- Full GC 触发原因(Allocation Failure?Metadata GC Threshold?)
- 各区域内存回收效率(Eden/Survivor 晋升率)
堆内存分布:老年代增长是否异常?
线程状态:WAITING等待线程/BLOCKED 阻塞线程占比

核心调优参数详解

1. 堆内存设置

-Xms4g -Xmx4g   # 初始堆=最大堆,避免动态扩容导致停顿
-XX:MaxMetaspaceSize=512m  # 元空间上限(防OOM)
-Xmn1.5g        # 新生代大小(G1通常不设,其他收集器建议占堆1/3~1/2)

2.选择合适的 GC 算法(GC收集器)(JDK 17+ 推荐 G1)

收集器适用场景关键参数
G1 (默认)平衡吞吐/延迟(JDK9+默认)-XX:MaxGCPauseMillis=200(目标停顿)
-XX:G1HeapRegionSize=4m(Region大小)
ZGC超低延迟(TB级堆)-XX:+UseZGC
-XX:ConcGCThreads=4
(并发线程)
Shenandoah低延迟(与ZGC竞争)-XX:+UseShenandoahGC
Parallel GC高吞吐量(批处理)-XX:+UseParallelGC
CMS (已废弃)JDK14前老应用低延迟方案不推荐新项目使用

3. 优化分代比例(非G1时)

-XX:SurvivorRatio=8          # Eden:Survivor=8:1(默认)
-XX:NewRatio=2               # 老年代:新生代=2:1
-XX:MaxTenuringThreshold=15  # 对象晋升老年代的年龄

1.新生代和老年代的比例是1:2,新生代占3分之一,老年代占3分之2
2.新生代里面的Eden区和from,to区的比例是8:1:1

3.如果创建大量的短暂存活对象,可以增大新生代的空间
4.如果创建大量长久存活对象的时候, 可以增大老年代的空间
5.调整比例是为了优化垃圾回收的效率
6.但是新生代过大,会导致频繁的垃圾回收,影响系统性能
7.如果老年代过大,会导致频繁的full gc,也会影响系统性能
8.所以一般不建议调整比例

4. 触发 Full GC 的关键点

FullGC是指会回收整个堆的内存,包含老年代、新生代、Metaspace等
FullGC的触发条件包括:

  • 元空间不足-XX:MetaspaceSize=256m(初始大小)
  • System.gc() 调用-XX:+DisableExplicitGC(禁用)
  • 老年代空间不足 → 检查内存泄漏或增大堆。

JVM的GC(垃圾回收)既可以从新生代开始,也可以从老年代开始

JVM中的堆一般分为新生代、老年代和永久代(在Java 8及以后版本中,永久代被元空间取代)。GC主要有两种类型:Minor GC和Major GC(或Full GC)123

Minor GC:主要发生在新生代,特别是Eden区和Survivor区之间的对象进行垃圾回收。当Eden区满时,JVM会触发Minor GC。Minor GC采用复制算法,将Eden和From Survivor区域中存活的对象复制到To Survivor区域,并将这些对象的年龄+1。如果对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁)时,就会被晋升到老年代中123

Major GC / Full GC:主要发生在老年代,当老年代空间不足时就会触发Major GC。Major GC采用标记—清除算法,首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。Major GC的耗时比较长,因为要扫描再回收。Major GC会产生内存碎片,为了减少内存损耗,通常需要进行合并或者标记出来方便下次直接分配

高级调优技巧

1. 避免大对象直接进老年代

   -XX:PretenureSizeThreshold=1m  # >1MB 对象直接在老年代分配

2. G1 优化建议

   -XX:InitiatingHeapOccupancyPercent=45  # 老年代占用45%时启动并发标记-XX:G1MixedGCLiveThresholdPercent=85   # 存活对象>85%的Region不回收

3. 减少 STW 停顿

STW(Stop-The-World)是指在垃圾回收期间,为了准确地识别和清理不再使用的对象,垃圾收集器需要暂停所有用户线程的执行,这个暂停过程就被称为STW

   -XX:+UseStringDeduplication     # G1字符串去重(节省内存)-XX:+AlwaysPreTouch             # 启动时预分配物理内存(避免运行时缺页)

4. 容器环境(Docker/K8s)

   -XX:+UseContainerSupport        # 识别容器内存限制(必须!)-XX:MaxRAMPercentage=75.0       # 使用容器内存的75%

常见问题与解决

现象可能原因解决方案
Full GC 频繁内存泄漏 / 老年代过小MAT分析堆快照 → 修复代码 / 增大堆
Young GC 耗时长Survivor 空间不足 → 对象过早晋升增大 -Xmn 或调整 -XX:SurvivorRatio
请求延迟毛刺GC 停顿导致切换低延迟GC(ZGC/Shenandoah)
Metaspace OOM动态类加载过多(如Spring)增大 -XX:MaxMetaspaceSize
CPU 持续高负载GC线程占用 / 代码死循环top -Hp 查线程 → jstack 定位

调优原则

  1. 优先监控分析,而非盲目调参
  2. 每次只改一个参数,观察效果
  3. 优先解决代码问题(如内存泄漏)
  4. 理解 GC 原理 > 死记参数
  5. 生产环境灰度验证

📌 示例:电商应用调优参数(JDK17+G1)

-Xms8g -Xmx8g  
-XX:+UseG1GC  
-XX:MaxGCPauseMillis=150  
-XX:InitiatingHeapOccupancyPercent=40  
-XX:+UseContainerSupport  
-Xlog:gc*,gc+heap=debug:file=/logs/gc.log:time,uptime:filecount=5,filesize=100m

调优是持续过程,建议结合 APM 工具(如 SkyWalking、Prometheus + Grafana)长期监控。遇到具体问题时,提供 GC 日志和异常现象,能更精准定位原因。

http://www.dtcms.com/a/271688.html

相关文章:

  • 双指针-15.三数之和-力扣(LeetCode)
  • AI技术如何重塑你的工作与行业?——实战案例解析与效率提升路径
  • gdb调试工具
  • Lingo软件学习(一)好学爱学
  • DPDK graph图节点处理框架:模块化数据流计算的设计与实现
  • dify配置邮箱,密码重置以及邮箱邀请加入
  • 【Java】【字节面试】字符串中 出现次数最多的字符和 对应次数
  • HTML应用指南:利用GET请求获取全国山姆门店位置信息
  • 跨服务sqlplus连接oracle数据库
  • 如何卸载本机的node.js
  • 源码角度解析 --- HashMap 的 get 和 put 流程
  • 前端使用fetch-event-source实现AI对话
  • AI Agent:我的第一个Agent项目
  • 爬虫-数据解析
  • [C语言初阶]操作符
  • ZeroMQ 代理架构实现(Python 服务端 + C++ 代理 + C++ 客户端)
  • RabbitMQ 4.1.1-Local random exchange体验
  • 解决Ollama下载太慢问题
  • Claude Code 环境搭建教程
  • Java SE--继承
  • Python 机器学习核心入门与实战进阶 Day 7 - 复盘 + 综合实战挑战
  • NW658NW659美光固态闪存NW660NW708
  • 陶哲轩:数学界的莫扎特与跨界探索者
  • 离线二维码生成器,无需网络快速制作
  • 神经网络基础及API使用详解
  • 【项目经理】经典面试题0002:项目经理和交付经理的区别?
  • 变频器实习DAY3
  • S7-1500——(一)西门子PLC编程从入门到精通3、基于TIA 博途结构化控制语言——SCL(一)
  • 全连接神经网络(MLP)原理与PyTorch实现详解
  • 【freertos-kernel】MemMang