news 2026/5/1 10:39:30

JVM调优实战及常量池详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM调优实战及常量池详解


在 Java 应用性能优化中,JVM 调优和常量池管理是两个关键环节。本文将深入解析 Arthas 工具、GC 日志分析、JVM 参数设置,以及 Class 常量池与运行时常量池的原理与应用,助您掌握 JVM 调优的核心技能。

一、Arthas 工具详解

1. 简介

Arthas 是阿里巴巴在 2018 年 9 月开源的 Java 诊断工具,支持 JDK6+,采用命令行交互模式,可以方便地定位和诊断线上程序运行问题。

核心特点​:

  • 无需重启应用即可诊断问题
  • 功能丰富,支持多种诊断场景
  • 交互式命令行界面,使用简单

2. 安装与使用

# 下载Arthaswget<https://alibaba.github.io/arthas/arthas-boot.jar># 运行Arthasjava -jar arthas-boot.jar

运行后,Arthas 会列出当前机器上所有 Java 进程,选择目标进程 ID 即可进入交互模式。

3. 常用命令

命令说明使用场景
dashboard查看进程运行状况(线程、内存、GC、运行环境)全局视角查看系统运行状况
thread查看线程详细情况分析线程状态、CPU 占用
thread -b查看线程死锁检测线程死锁
jad反编译类查看线上代码是否是正确版本
ognl执行表达式更灵活的代码调试

4. 使用场景

Arthas 可以帮助解决以下常见问题:

  • 是否有一个全局视角来查看系统的运行状况?
  • 为什么 CPU 又升高了,到底是哪里占用了 CPU?
  • 运行的多线程有死锁吗?有阻塞吗?
  • 程序运行耗时很长,是哪里耗时比较长呢?
  • 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  • 我改的代码为什么没有执行到?难道是我没 commit?
  • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  • 有什么办法可以监控到 JVM 的实时运行状态?

二、GC 日志详解

1. 打印 GC 日志方法

在 JVM 参数中添加以下参数,即可打印 GC 日志:

-Xloggc:./gc-%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10-XX:GCLogFileSize=100M

对于 Tomcat,直接加在 JAVA_OPTS 变量里即可。

2. GC 日志解析

以下是一个 GC 日志示例:

2023-05-15T14:30:22.123+0800: 2.909: [Full GC (Metadata GC Threshold) 6160K->0K(141824K), 0.0209707 secs]

关键信息解析​:

  • 2.909:从 JVM 启动开始计算到这次 GC 经过的时间
  • Full GC (Metadata GC Threshold):这是一次 Full GC,原因是元空间不足
  • 6160K->0K(141824K):GC 前年轻代占用 6160K,GC 后占用 0K,年轻代总大小 141824K
  • 112K->6056K(95744K):GC 前老年代占用 112K,GC 后占用 6056K,老年代总大小 95744K
  • 0.0209707 secs:GC 总耗时 0.0209707 秒

3. GC 日志分析工具

​**gceasy (https://gceasy.io)**​:

  • 可以上传 GC 日志文件
  • 提供可视化的 GC 分析界面
  • 展示年轻代、老年代、永久代的内存分配和最大使用情况
  • 提供 JVM 智能优化建议(部分功能需付费)

4. GC 日志优化案例

问题​:元空间不够导致频繁 Full GC

优化前参数​:

-Xms1536M -Xmx1536M -Xmn512M...

优化后参数​:

-Xloggc:./gc-adjust-%t.log -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M...

效果​:优化后 GC 日志中不再出现因元空间不足导致的 Full GC。

三、JVM 参数汇总

1. 查看 JVM 参数

# 查看默认参数java -XX:+PrintFlagsInitial# 查看生效参数java -XX:+PrintFlagsFinal

2. 常用 JVM 参数

参数说明适用场景
-Xms初始堆大小优化内存分配
-Xmx最大堆大小避免内存溢出
-Xmn年轻代大小优化 GC 频率
-XX:SurvivorRatioSurvivor 区比例减少对象进入老年代
-XX:MaxTenuringThreshold对象晋升老年代阈值优化对象生命周期
-XX:PretenureSizeThreshold大对象直接进入老年代阈值避免大对象占用 Survivor 区
-XX:MetaspaceSize元空间初始大小避免元空间 Full GC
-XX:MaxMetaspaceSize元空间最大大小控制元空间使用

四、Class 常量池与运行时常量池

1. Class 常量池

Class 常量池是 Class 文件中的资源仓库,包含编译期生成的各种字面量和符号引用。

Class 文件 16 进制结构​:

  • 类版本
  • 字段
  • 方法
  • 接口
  • 常量池

常量池主要包含两类常量​:

  • 字面量:字符串、数值等
  • 符号引用:类和接口全限定名、字段名称和描述符、方法名称和描述符

2. 字面量

字面量是指由字母、数字等构成的字符串或者数值常量,只能作为右值出现。

示例​:

inta=1;// 1是字面量intb=2;// 2是字面量Stringc="abc";// "abc"是字面量

3. 符号引用

符号引用是编译原理中的概念,主要包括:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

示例​:

publicclassMath{publicintcompute(){...}}

在常量池中,Lcom/tuling/jvm/Math是类的全限定名,compute是方法名称,()是描述符。

4. 运行时常量池

当 Class 被加载到内存后,常量池就变成了运行时常量池,符号引用在程序加载或运行时会被转变为直接引用(动态链接)。

示例​:

  • compute()这个符号引用在运行时会被转变为compute()方法在内存中的地址
  • 通过对象头里的类型指针进行转换

五、字符串常量池

1. 设计思想

字符串的分配和其他对象分配一样,耗费高昂的时间与空间代价。JVM 为了提高性能和减少内存开销,为字符串开辟了常量池。

设计原理​:

  • 创建字符串时,先查询字符串常量池
  • 如果存在,返回引用实例
  • 如果不存在,创建字符串对象并放入池中

2. 字符串常量池位置

JDK 版本字符串常量池位置
JDK 6 及之前永久代(PermGen)
JDK 7从永久代分离到堆中
JDK 8 及之后堆中

验证代码​:

publicclassRuntimeConstantPoolOOM{publicstaticvoidmain(String[]args){ArrayList<String>list=newArrayList<String>();for(inti=0;i<10000000;i++){Stringstr=String.valueOf(i).intern();list.add(str);}}}

运行结果​:

  • JDK 7 及以上:java.lang.OutOfMemoryError: Java heap space
  • JDK 6:java.lang.OutOfMemoryError: PermGen space

3. 三种字符串操作

操作方式代码示例说明
直接赋值String s = "zhuge";只会在常量池中创建
new String()String s1 = new String("zhuge");常量池和堆中都有对象
intern 方法String s2 = s1.intern();返回常量池中的引用

4. 字符串常量池问题示例

示例 1:编译期优化

Strings0="zhuge";Strings1="zhuge";Strings2="zhu"+"ge";System.out.println(s0==s1);// trueSystem.out.println(s0==s2);// true

原理​:"zhu""ge"都是字符串常量,连接后也是字符串常量,编译期就确定了。

示例 2:new String()创建

Strings0="zhuge";Strings1=newString("zhuge");Strings2="zhu"+newString("ge");System.out.println(s0==s1);// falseSystem.out.println(s0==s2);// falseSystem.out.println(s1==s2);// false

原理​:new String()创建的对象不在常量池中,无法在编译期确定。

示例 3:编译期优化

Stringa="a1";Stringb="a"+1;System.out.println(a==b);// trueStringa="atrue";Stringb="a"+"true";System.out.println(a==b);// true

原理​:JVM 在编译期将常量字符串连接优化为连接后的值。

示例 4:运行期动态连接

Stringa="ab";Stringbb="b";Stringb="a"+bb;System.out.println(a==b);// false

原理​:由于有字符串引用存在,编译期无法确定,需要在运行期动态连接。

示例 5:final 修饰

Stringa="ab";finalStringbb="b";Stringb="a"+bb;System.out.println(a==b);// true

原理​:final 变量在编译时被解析为常量值。

示例 6:方法返回值

Stringa="ab";finalStringbb=getBB();Stringb="a"+bb;System.out.println(a==b);// false

原理​:方法返回值在编译期无法确定,需要在运行期动态连接。

5. String 不可变性

示例​:

Strings="a"+"b"+"c";// 等价于String s = "abc";Stringa="a";Stringb="b";Stringc="c";Strings1=a+b+c;// 不是"abc",而是通过StringBuilder拼接

JVM 指令码​:

StringBuildertemp=newStringBuilder();temp.append(a).append(b).append(c);Strings=temp.toString();

六、基本类型包装类与对象池

1. 实现对象池的包装类

Byte, Short, Integer, Long, Character, Boolean 实现了对象池技术(在堆上)。

特点​:

  • 仅在值小于等于 127 时使用对象池
  • 一般较小的数值使用概率较大

2. 浮点类型包装类

Double 和 Float 没有实现对象池技术。

3. 对象池示例

publicclassTest{publicstaticvoidmain(String[]args){// 5种整型包装类,在值小于127时使用对象池Integeri1=127;// Integer.valueOf(127)Integeri2=127;System.out.println(i1==i2);// true// 值大于127,不使用对象池Integeri3=128;Integeri4=128;System.out.println(i3==i4);// false// new关键词创建对象,不使用对象池Integeri5=newInteger(127);Integeri6=newInteger(127);System.out.println(i5==i6);// false// Boolean实现对象池Booleanbool1=true;Booleanbool2=true;System.out.println(bool1==bool2);// true// 浮点类型没有实现对象池Doubled1=1.0;Doubled2=1.0;System.out.println(d1==d2);// false}}

七、JVM 常量池与调优实战

1. 字符串常量池优化建议

  • ​**避免使用 new String()**​:除非确实需要在堆中创建新对象
  • ​**合理使用 intern()**​:在需要确保唯一性时使用
  • 注意字符串连接​:小字符串连接在编译期优化,大字符串连接在运行期优化

2. 常量池调优建议

  • 合理设置元空间大小​:避免因元空间不足导致 Full GC
  • 优化字符串常量池​​:减少不必要的字符串创建
  • 注意常量池对内存的影响​:特别是大项目中,常量池可能占用较多内存

3. 实战案例

问题​:某电商平台在高峰期出现 Full GC 频繁,系统响应变慢。

分析​:

  • 使用 Arthas 查看线程和内存
  • 通过 GC 日志分析,发现元空间不足导致 Full GC
  • 使用 jmap 查看内存,发现字符串常量池占用过大

优化​:

  1. 增大元空间:XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
  2. 优化字符串使用:避免在循环中创建大量字符串
  3. 优化代码:减少不必要的字符串拼接

效果​:

  • Full GC 频率从每 5 分钟 1 次 → 每小时 1 次
  • 系统响应时间从 500ms → 200ms
  • 内存使用率从 80% → 60%

八、总结与建议

1. JVM 常量池核心要点

  • Class 常量池​​:Class 文件中的资源仓库,包含字面量和符号引用
  • 运行时常量池​​:Class 常量池被加载到内存后的形式
  • 字符串常量池​​:JVM 为字符串开辟的缓存区,减少字符串创建开销
  • 基本类型包装类​:Byte、Short、Integer 等在小数值时使用对象池

2. JVM 调优建议

  • 了解应用特性​:根据应用特点选择合适的 GC 算法
  • 监控是基础​:没有监控,优化就是盲人摸象
  • 分步优化​:不要一次性调整太多参数,逐步验证效果

3. 重要提醒

  • 默认参数已优化​:JDK 8+ 的默认参数已考虑了大多数场景
  • 不要过度调优​:过度调优可能导致问题
  • 测试环境验证​:在生产环境实施前,务必在测试环境验证效果

“JVM 常量池和调优不是魔法,而是有规律可循的系统。理解了常量池的原理、掌握了调优方法,你就能在 Java 应用性能优化的道路上走得更远。”

实战建议清单

问题类型诊断方法解决方案
Full GC 频繁GC 日志分析优化元空间大小,调整 JVM 参数
字符串占用高jmap 分析优化字符串使用,减少不必要的创建
常量池过大jmap 分析合理使用字符串常量,避免重复创建
对象池失效代码分析注意包装类的使用范围,避免超出对象池范围

最后提醒​:在实施 JVM 调优前,务必在测试环境验证效果。一个错误的 JVM 参数可能导致生产环境严重问题,而正确的优化能带来 10 倍性能提升。

“当你能读懂 JVM 常量池原理、理解调优方法、掌握实战技巧,你就真正掌握了 Java 应用的性能优化。从源码到执行,这是一条充满智慧的道路。”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 21:14:41

Local SDXL-Turbo一文详解:对抗扩散蒸馏(ADD)技术原理与实测

Local SDXL-Turbo一文详解&#xff1a;对抗扩散蒸馏&#xff08;ADD&#xff09;技术原理与实测 1. 什么是Local SDXL-Turbo&#xff1f;它为什么能“打字即出图” 你有没有试过在AI绘画工具里输入提示词&#xff0c;然后盯着进度条等上5秒、10秒&#xff0c;甚至更久&#x…

作者头像 李华
网站建设 2026/5/1 6:06:14

COMSOL模拟压裂水平井离散裂缝瓦斯抽采

COMSOL模拟压裂水平井的离散裂缝的瓦斯抽采。 最近在搞压裂水平井的瓦斯抽采模拟&#xff0c;用COMSOL折腾了半天离散裂缝模型&#xff0c;踩了不少坑。今天就跟大伙唠唠怎么用裂缝流模块整活&#xff0c;特别是离散裂缝网络的处理技巧。老规矩&#xff0c;先搭几何模型。 压…

作者头像 李华
网站建设 2026/4/17 18:31:45

5个高效技巧:gerbv让PCB工程师实现精准Gerber验证

5个高效技巧&#xff1a;gerbv让PCB工程师实现精准Gerber验证 【免费下载链接】gerbv Maintained fork of gerbv, carrying mostly bugfixes 项目地址: https://gitcode.com/gh_mirrors/ge/gerbv 在PCB设计流程中&#xff0c;Gerber文件作为连接设计与制造的关键桥梁&am…

作者头像 李华
网站建设 2026/5/1 6:08:51

如何解锁处理器隐藏性能?专业调试工具全攻略

如何解锁处理器隐藏性能&#xff1f;专业调试工具全攻略 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcode.com…

作者头像 李华
网站建设 2026/4/30 6:07:21

YOLOE镜像无提示模式实测,自动发现所有目标

YOLOE镜像无提示模式实测&#xff0c;自动发现所有目标 你有没有遇到过这样的场景&#xff1a;一张杂乱的街景图里&#xff0c;有行人、自行车、广告牌、垃圾桶、飞鸟、甚至远处模糊的无人机——但你根本不知道该让模型“找什么”&#xff0c;更不想一个个手动输入类别名&#…

作者头像 李华
网站建设 2026/4/30 14:38:19

播客制作利器!用FSMN-VAD自动剪掉空白段

播客制作利器&#xff01;用FSMN-VAD自动剪掉空白段 你有没有过这样的经历&#xff1a;录完一小时的播客&#xff0c;发现其中近20分钟全是“嗯”“啊”“让我想想”、翻纸声、键盘敲击声&#xff0c;甚至还有长达8秒的沉默&#xff1f;手动剪辑这些空白段&#xff0c;不仅耗时…

作者头像 李华