JVM调优实战及常量池详解

news/2025/9/29 17:47:38/文章来源:https://www.cnblogs.com/randolf/p/19119229

JVM调优实战及常量池详解

一、阿里巴巴 Arthas 工具

Arthas 是 Alibaba 开源的 Java 诊断工具(支持 JDK6+),采用命令行交互,可快速定位线上问题,核心内容如下:

1. 下载与启动

# GitHub下载
wget https://alibaba.github.io/arthas/arthas-boot.jar
# Gitee下载(国内更快)
wget https://arthas.gitee.io/arthas-boot.jar
  • 启动步骤
    1. 执行java -jar arthas-boot.jar,工具自动识别当前机器所有 Java 进程;
    2. 输入进程对应的序号(如1),进入该进程的 Arthas 交互界面。

2. 核心命令与功能

命令格式 功能描述 实战场景示例
dashboard 实时展示进程的线程、内存、GC、运行环境信息(如 % CPU、堆内存 used/total) 快速定位高 CPU 线程(如 Thread-0 占 CPU 97%)
thread 查看所有线程状态;thread <线程ID>查看指定线程堆栈;thread -b检测死锁 thread -b发现 Thread-1 与 Thread-2 互锁资源(分别持有 resourceA/resourceB)
jad <类全限定名> 反编译线上类,验证代码版本是否正确 jad com.tuling.jvm.Arthas查看线上 Arthas 类的实际代码
ognl "@类名@属性.方法()" 操作类的静态属性 / 方法(如添加数据到静态集合) ognl "@com.tuling.jvm.Arthas@hashSet.add('test123')"往静态 hashSet 加数据

3. 实战案例

public class ArthasTest {private static HashSet hashSet = new HashSet();public static void main(String[] args) {//模拟CPU过高cpuHigh();// 模拟线程死锁deadThread();// 不断的向 hashSet 集合增加数据addHashSetThread();}/*** 不断的向 hashSet 集合添加数据*/public static void addHashSetThread() {// 初始化常量new Thread(() -> {int count = 0;while (true) {try {hashSet.add("count" + count);Thread.sleep(1000);count++;} catch (InterruptedException e) {e.printStackTrace();}}},"thread1").start();}public static void cpuHigh() {new Thread(() -> {while (true) {}},"thread2").start();}/*** 死锁*/private static void deadThread() {/** 创建资源 */Object resourceA = new Object();Object resourceB = new Object();// 创建线程Thread threadA = new Thread(() -> {synchronized (resourceA) {System.out.println(Thread.currentThread() + " get ResourceA");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resourceB");synchronized (resourceB) {System.out.println(Thread.currentThread() + " get resourceB");}}},"threadA");Thread threadB = new Thread(() -> {synchronized (resourceB) {System.out.println(Thread.currentThread() + " get ResourceB");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resourceA");synchronized (resourceA) {System.out.println(Thread.currentThread() + " get resourceA");}}},"threadB");threadA.start();threadB.start();}
}

Arthas测试类,模拟三类问题,用 Arthas 排查:

  • CPU 过高cpuHigh()方法创建空循环线程,通过dashboard发现该线程(Thread-0)% CPU 达 97%,thread 8(线程 ID)定位到空循环代码;
  • 线程死锁deadThread()方法中 ThreadA 锁 resourceA 等 resourceB,ThreadB 锁 resourceB 等 resourceA,thread -b直接检测到死锁及锁持有关系;
  • 动态数据添加addHashSetThread()方法每秒往静态 hashSet 加数据,用ognl可实时操作该集合,验证数据添加逻辑。

二、GC 日志详解

通过配置 JVM 参数打印 GC 日志,分析 GC 原因与性能瓶颈,核心内容如下:

1. GC 日志配置参数

参数名称 作用 说明
-Xloggc:./gc-%t.log 指定 GC 日志输出路径,%t为时间戳 避免日志覆盖,如生成gc-20240520.log
-XX:+PrintGCDetails 打印详细 GC 信息(区域内存变化、耗时) 必配参数,核心分析依据
-XX:+PrintGCDateStamps 打印 GC 发生的具体日期时间(如 2019-07-03T17:28:24) 便于定位时间点相关问题
-XX:+PrintGCTimeStamps 打印 GC 发生时 JVM 启动后的耗时(如 0.613 秒) 计算 GC 频率
-XX:+UseGCLogFileRotation 启用 GC 日志轮转(避免单日志过大) 配合以下两个参数使用
-XX:NumberOfGCLogFiles=10 日志文件最大数量为 10 个 超过后覆盖旧日志
-XX:GCLogFileSize=100M 单个日志文件最大大小为 100MB 达到大小后生成新日志

2. GC 日志解读(以 Parallel GC 为例)

示例日志片段:

2019-07-03T17:28:24.889+0800:0.613:[GC (Allocation Failure) [PSYoungGen:65536K->3872K(76288K)]65536K->3888K(251392K), 0.0042006 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  • 时间信息2019-07-03T17:28:24.889+0800(具体时间)、0.613(JVM 启动后 0.613 秒);
  • GC 类型与原因GC (Allocation Failure)(Minor GC,原因是内存分配失败);
  • 内存变化
    • PSYoungGen:65536K->3872K(76288K):年轻代 GC 前占用 65536K,GC 后 3872K,总大小 76288K;
    • 65536K->3888K(251392K):堆内存 GC 前 65536K,GC 后 3888K,总大小 251392K;
  • 耗时0.0042006 secs(GC 总耗时,单位秒)。

3. 日志分析与优化

  • 常见问题定位:若日志中频繁出现Full GC (Metadata GC Threshold),说明元空间不足,需调整参数-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
  • 可视化工具:通过gceasy.io上传日志,生成可视化报告(如年轻代 / 老年代内存分配、GC 时长趋势),并提供智能优化建议(如 G1 GC 的-XX:InitiatingHeapOccupancyPercent调整)。

三、JVM 参数查看命令

通过以下命令查看 JVM 参数的默认值与运行时生效值,用于验证参数配置:

命令格式 功能描述 应用场景
java -XX:+PrintFlagsInitial 打印所有 JVM 参数的默认值 了解参数默认配置(如InitialHeapSize默认值)
java -XX:+PrintFlagsFinal 打印所有 JVM 参数在运行时的生效值 验证参数是否正确生效(如-Xms10M是否生效)

四、常量池详解

常量池分为 Class 常量池、运行时常量池、字符串常量池,核心差异与特点如下:

1. Class 常量池与运行时常量池

  • Class 常量池
    • 位置:Class 文件中,是 Class 文件的 “资源仓库”;
    • 内容:存放编译期生成的字面量(如1"zhuge")和符号引用(类全限定名、字段 / 方法名称及描述符);
    • 查看方式:javap -v 类名.class(生成可读字节码,展示Constant pool section)。
  • 运行时常量池
    • 位置:类加载后进入内存(JDK1.6 在永久代,JDK1.7 + 在堆);
    • 功能:将 Class 常量池的符号引用转为直接引用(动态链接,如compute()方法符号引用转为内存地址)。

2. 字符串常量池

  • 核心设计:JVM 为优化字符串创建效率,开辟独立的字符串常量池(类似缓存),创建字符串时优先复用池中对象。

  • 位置变化(关键差异):

    JDK 版本 字符串常量池位置 intern () 方法行为
    JDK1.6 及之前 永久代(PermGen) 池中无该字符串时,复制堆对象到永久代,返回永久代引用
    JDK1.7 及之后 堆(Heap) 池中无该字符串时,直接指向堆对象,返回堆引用
  • 三种创建方式对比

    创建方式 常量池是否创建对象 堆是否创建对象 返回引用指向
    String s = "zhuge"; 无则创建 不创建 常量池对象
    String s = new String("zhuge"); 无则创建 必创建 堆对象
    s.intern() 无则关联堆对象 不创建 常量池引用
  • 特殊案例

    String s1 = new StringBuilder("ja").append("va").toString();
    System.out.println(s1 == s1.intern()); // JDK1.7+输出false
    

    原因:“java” 是关键字,JVM 初始化时已放入字符串常量池,

    s1
    

    指向堆对象,

    s1.intern()
    

    指向常量池对象,故不相等。

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

为优化基本类型包装类的创建效率,部分包装类实现对象池技术,核心规则如下:

1. 对象池实现情况

包装类类型 是否实现对象池 生效范围 示例代码与结果
Byte 所有值(-128~127) Byte b1=127; Byte b2=127; System.out.println(b1==b2); // true
Short 值≤127 Short s1=127; Short s2=127; System.out.println(s1==s2); // true
Integer 值≤127(默认范围,可通过参数调整) Integer i1=127; Integer i2=127; System.out.println(i1==i2); // true
Long 值≤127 Long l1=127; Long l2=127; System.out.println(l1==l2); // true
Character 值≤127 Character c1='a'; Character c2='a'; System.out.println(c1==c2); // true
Boolean 所有值(true/false) Boolean bool1=true; Boolean bool2=true; System.out.println(bool1==bool2); // true
Float Float f1=1.0f; Float f2=1.0f; System.out.println(f1==f2); // false
Double Double d1=1.0; Double d2=1.0; System.out.println(d1==d2); // false

2. 关键注意点

  • new创建包装类对象时,不使用对象池(如new Integer(127)会新创建对象,==比较为 false);
  • 整型包装类的对象池范围可通过 JVM 参数-XX:AutoBoxCacheMax=<size>调整(仅 Integer 支持)。

关键问题

问题 1:在 JDK1.8 环境下,如何用 Arthas 完整排查 “线上 Java 进程 CPU 占用过高” 的问题?请结合文档步骤说明。

答案:需通过 “定位高 CPU 线程→查看线程堆栈→关联代码” 三步排查,具体如下:

  1. 启动 Arthas 并进入进程

    执行java -jar arthas-boot.jar,输入高 CPU 进程对应的序号(如1),进入交互界面;

  2. 定位高 CPU 线程

    输入命令dashboard,查看 “% CPU” 列,找到 CPU 占比最高的线程(如 Thread-0,% CPU=97%),记录其线程 ID(如8);

  3. 查看线程堆栈

    输入命令thread 8(线程 ID),查看该线程的堆栈信息,定位到具体代码行(如com.tuling.jvm.Arthas.Lambda$cpuHigh$1(Arthas.java:39),发现是空循环导致 CPU 过高);

  4. 验证代码(可选)

    若怀疑代码版本问题,输入jad com.tuling.jvm.Arthas反编译线上类,确认cpuHigh()方法是否存在空循环逻辑,最终定位问题根源。

问题 2:字符串常量池在 JDK1.6 与 JDK1.7 + 的位置和intern()方法行为有何核心差异?请结合示例代码说明。

答案:核心差异体现在 “常量池位置” 和 “intern () 对象处理逻辑”,具体如下:

对比维度 JDK1.6 及之前 JDK1.7 及之后
常量池位置 永久代(PermGen) 堆(Heap)
intern () 行为 池中无该字符串时,复制堆对象到永久代,返回永久代引用 池中无该字符串时,直接指向堆对象,返回堆引用

示例代码验证

String s1 = new StringBuilder("zhuge").toString(); // 堆创建对象,常量池无"zhuge"
String s2 = s1.intern(); 
System.out.println(s1 == s2); 
  • JDK1.6 输出falses1指向堆对象,s2指向永久代中复制的新对象,地址不同;
  • JDK1.7 + 输出trues1指向堆对象,s2直接指向该堆对象(常量池关联堆引用),地址相同。

问题 3:Java 中 8 种基本类型的包装类中,哪些实现了对象池技术?其生效范围是什么?为何浮点数包装类未实现对象池?

答案

  1. 实现对象池的包装类及生效范围

    共 6 种包装类实现对象池,具体如下:

    包装类 生效范围 核心说明
    Byte 所有值(-128~127) 范围固定,无调整空间
    Short 值≤127 仅小值复用,大于 127 时新创建对象
    Integer 值≤127(默认,可通过-XX:AutoBoxCacheMax调整) 最常用,默认范围覆盖多数场景
    Long 值≤127 同 Short,仅小值复用
    Character 值≤127(ASCII 码范围内) 覆盖常用字符(如字母、数字)
    Boolean 所有值(true/false) 仅两个值,完全复用
  2. 浮点数包装类(Float、Double)未实现对象池的原因

    浮点数的取值范围极广(如 Float 可表示约 3.4×10³⁸的数),且小值的复用概率远低于整型(如业务中很少频繁使用1.02.0等固定浮点数),实现对象池的 “收益(内存节省)” 远小于 “成本(池维护开销)”,因此 JVM 未为其实现对象池技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/922076.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

做asp网站需要的实验报告单上海网站建设置作

我来教你 js文件怎么通过python访问数据库&#xff0c;希望能够为你带来帮助。 1、如果是要提交表单内容给 服务器的 python 处理&#xff0c;那么只需要在表单 里面的 action 填写服务端的处理路由就可以了。或者用 指向服务器路由get querystring。前端方面不需要python啊。…

个人备案网站建设方案书网络营销推广怎么做

string模拟实现 构造函数和析构函数begin和endreserve和resizepush_back和appendc_strempty&#xff0c;size&#xff0c;capacity&#xff0c;clear拷贝构造和赋值和比较大小[]重载insert和erasefind查找 前面我们已经对string进行了简单的介绍&#xff0c;只要会用各个函数即…

Cisco Identity Services Engine (ISE) 3.5 - 基于身份的网络访问控制和策略实施系统

Cisco Identity Services Engine (ISE) 3.5 - 基于身份的网络访问控制和策略实施系统Cisco Identity Services Engine (ISE) 3.5 - 基于身份的网络访问控制和策略实施系统 思科身份服务引擎 (ISE) - 下一代 NAC 解决方…

[源码阅读][vmselect] 从promql 到一条曲线,计算过程是怎么样的?

[源码阅读][vmselect] 从promql 到一条曲线,计算过程是怎么样的?作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客 zhihu Github 公众号:一本正经的瞎扯以最经典的计算 qps 的曲线为例,vmse…

kuboard使用的etcd空间满了如何处理

1.在master节点通过命令进入etcd容器,pod名称替换成自己的[root@master ~]# kubectl get pods -n kuboard NAME READY STATUS RESTARTS AGE kuboard-etcd-6vmkm 1/1 R…

国内信创领域的PostgreSQL技术能力认证

工信部人才交流中心颁发的 PostgreSQL 数据库管理员认证(以下简称 “工信人才 PG 认证”)是国内信创领域的技术能力认证,随着信创战略推进,PostgreSQL 作为开源可控的数据库代表,已成为国产替代的标杆技术。在中央企…

redis-AOF持久化机制

redis-AOF持久化机制AOF,Appedn Only File,指Redis将每一次的写操作都以日志的形式记录到一个AOF文件中的持久化技术。 当需要恢复内存数据时,只这些写操作重新执行一次就可以将之前的内存数据恢复。 AOF配置开启AO…

03-控制台项目创建与结构说明

项目创建 项目名称和存放位置 Main函数

从拆盒到共创:手办盲盒抽赏小程序的多元体验与文化联结 - 实践

从拆盒到共创:手办盲盒抽赏小程序的多元体验与文化联结 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

Nginx技术文档与LNMP架构部署指南 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

海康威视WEB视频监控插件3.3 解决视频画面遮挡 无法隐藏的问题 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

怎么做类似淘宝一样的网站吗wordpress维护服务

vue-cli项目首页加载缓慢想要使用骨架屏效果&#xff0c;经过几天的踩坑&#xff0c;这里学习并记录一下vue项目自动生成骨架屏方法。 添加骨架屏&#xff0c;其优势在于&#xff1a; 写于HTML文件中&#xff0c;独立于Vue框架&#xff0c;节省了JS加载时间JS全局环境创建的执…

YACS2025年9月乙组

YACS2025年9月乙组T1. 数学作业 发现 \(a-b\) 太大会很快超过题目所限的范围,所以 \(a-b\) 值并不大。 然后枚举差值 \(d\),发现 \(\frac{(x+d)!}{x!}\) 关于 \(x\) 单调递增。所以可以二分判断存不存在 \(x\) 满足 …

做网站需要源码网站主机购买

这篇文章主要介绍了oracle导入导出数据的二种方法&#xff0c;利用PL/SQL Developer工具导出和利用cmd的操作命令导出的出方法&#xff0c;大家参考使用吧方法一&#xff1a;利用PL/SQL Developer工具导出&#xff1a;菜单栏---->Tools---->Export Tables&#xff0c;如下…

赋能智慧应急:国标GB28181平台EasyGBS视频技术如何成为气象灾害预警新工具

赋能智慧应急:国标GB28181平台EasyGBS视频技术如何成为气象灾害预警新工具中国地理广阔,人口众多,自然环境复杂,气象灾害频发,是全球气象灾害最严重的国家之一。气象灾害约占自然灾害的70%,种类繁多、分布广泛,…

做视频免费模板下载网站seo网站导航建设技巧

##数据库事务 ###含义 通过一组逻辑操作单元&#xff08;一组DML——sql语句&#xff09;&#xff0c;将数据从一种状态切换到另外一种状态 ###特点 &#xff08;ACID&#xff09; 原子性&#xff1a;要么都执行&#xff0c;要么都回滚 一致性&#xff1a;保证…

NET各个版本新增的特性和语法糖

以下是按C#版本从低到高整理的.NET相关版本特性,补充了发布年份及对应的.NET Core/.NET版本信息,包含特性概念、作用、优势及示例: C# 6.0对应版本:.NET Framework 4.6(2015年)、.NET Core 1.0(2016年) 核心特…

xinference推理embedding等小模型

使用容器方式的xinference管理小模型,带鉴权、带本地模型加载embedding、rerank模型不少,需要一个框架来集中管理,选用了xinference,使用简单。采取容器化部署: 1、镜像下载:原始模型下载慢,采用渡渡鸟,下载 2…

day15-项目上线

今日内容 1 项目上线架构# 1 购买云服务---操作系统--》centos9-上线到公网 # 2 nginx-转发用户的请求--》到uwsgi的django项目# 3 mysql8-后端项目数据存储在mysql中# 4 上传我们后端项目-导入项目依赖:requirements…

Docker入门 - 实践

Docker入门 - 实践2025-09-29 17:19 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fam…