首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >JVM揭秘之旅:打破性能瓶的终极指南(1)

JVM揭秘之旅:打破性能瓶的终极指南(1)

作者头像
半旧518
发布2025-07-10 08:24:42
发布2025-07-10 08:24:42
1030
举报
文章被收录于专栏:半旧的技术栈半旧的技术栈

JVM揭秘之旅:打破性能瓶的终极指南(一)

专栏简介

「为什么Java程序员必须啃透JVM?」 JVM是Java生态的“灵魂引擎”,但多数开发者仅停留在API调用层面。当面临频发GC卡顿诡异OOM崩溃线程死锁顽疾时,是否曾因底层原理的模糊而束手无策?本专栏将带您穿透技术迷雾,系统攻克JVM核心领域:

  • ⚙️ 硬核原理拆解:从字节码执行、类加载双亲委派,到G1/ZGC回收器设计,逐层剖析JVM的运作机制;
  • 🛠️ 调优实战手册:结合大厂案例,详解参数配置(如-XX:+HeapDumpOnOutOfMemoryError)、内存泄漏定位(MAT工具)、并发瓶颈破解;
  • 🚀 前沿技术追踪:涵盖元空间、JIT编译、协程(Loom项目)等新特性,提前掌握未来技术栈;
  • 💡 面试高频攻略:深度解析京东/华为等大厂JVM面试题。

适合读者: ✅ 渴求突破CRUD的Java工程师 ✅ 被性能问题困扰的架构师 ✅ 备战P7/P8级技术面试的求职者

专栏承诺不用空洞理论堆砌,每篇均附可复现的代码案例及调优脚本。跟随专栏,您将获得从“被动救火”到“主动防御”的JVM掌控力!

1、JVM简介

什么是jvm

学习JVM有什么用

常见的JVM

以下JVM以Hotspot为准。

JVM的学习路线

2、程序计数器

程序计数器的作用

在物理上,程序计数器是通过寄存器实现的。

程序计数器的特点

线程私有。每个线程都有自己的程序计数器。不同线程抢cpu,抢到了,就根据程序计数器的地址,执行下一条代码。

不会存在内存溢出。 3、虚拟机栈

栈:先进后出。

虚拟机栈:线程运行时需要的内存空间。

栈帧:每个方法运行时需要的内存。

栈的演示

栈的问题辨析

1.垃圾回收不涉及栈内存。因为方法结束,栈帧的生命就结束了。

2.栈内存大,可以进行递归调用的层数多,但程序可执行线程越少(总内存不变,栈内存大,线程数少)

详解:

对于基本数据类型,安全。

局部变量存在栈中,属于线程私有。

对于对象。

判断下列三个方法是否线程安全?

m1,显然安全。局部变量线程私有。

m2,不安全,形参是被传入的,其它线程也可有访问到sb

m3,不安全。虽然sb是局部变量,但是被返回了,其它线程可以访问到。

栈内存溢出

1.栈帧过多。

实际一般是递归调用没有合理的递归终止条件。

有时候不是自己的代码有问题,而是没有正确引用第三方库。

比如转json时。

TypeScript import com.fasterxml.jackson.databind.ObjectMapper; class Dept { private String name; private Emp manager; // 构造方法、getter和setter public Dept(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Emp getManager() { return manager; } public void setManager(Emp manager) { this.manager = manager; } } class Emp { private String name; private Dept dept; // 构造方法、getter和setter public Emp(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Dept getDept() { return dept; } public void setDept(Dept dept) { this.dept = dept; } } public class StackOverflowDemo { public static void main(String[] args) throws Exception { // 创建相互引用的对象 Dept dept = new Dept(“研发部”); Emp emp = new Emp(“张三”); dept.setManager(emp); emp.setDept(dept); // 尝试序列化为JSON - 这将导致栈溢出 ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dept); System.out.println(json); } }

正确做法。

TypeScript class Emp { // … @JsonIgnore public Dept getDept() { return dept; } }

2.栈帧过大导致内存溢出。一般不会出现。

线程运行诊断——cpu运行高

top命令定位到进程。

定位到线程。

H 表示以"线程模式"显示信息。默认 ps 只显示进程,加上 H 后会显示进程中的所有线程

-e 显示所有用户进程,包括其它用户进程。

-o 自定义输出格式

知道了线程编号,接下来。定位到代码中的线程名

先将线程id32665换算成16进制。

Jstack 进程id.

看到没,第8行代码。

线程死锁排查——程序运行很长时间无结果

nohub在后台运行代码。

本来应该输出结果,一直没有。可能是死锁。

上面运行代码,可以看到进程id 32752.

Jstack 32752.

报错信息很明显。

根据上面信息的代码行数定位下问题。

很明显,它们互锁了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JVM揭秘之旅:打破性能瓶的终极指南(一)
  • 专栏简介
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档