周日早上七点的告警
故事从周日早上七点开始:值班群里 OOM 告警炸了,堆内存 100%,服务挂着几十个线程在哀嚎。值班的同事熟练地重启,服务复活;周一风平浪静,周三同一告警再次响起,又是重启。一个月里这台服务被重启了六次,没人知道它为什么吃内存,只知道重启能续命。
这不是段子,是很多团队的日常。会写 Java 和会养 Java 是两回事——代码能跑起来靠的是 IDE 和框架,进程能活得健康靠的是对 JVM 的理解。重启大法的本质,是看不懂病历只管掐人中。
JVM 到底管着什么
Java 号称"一次编写,到处运行",但代码从来不是直接跑在操作系统上——你写的每一行都跑在 JVM 这个地盘上。它管三件事:内存(对象分配在哪、什么时候回收)、执行(字节码解释执行还是编译成机器码)、运行时服务(类怎么加载、线程怎么调度)。GC、OOM、StackOverflowError,全藏在这三件事里。
// 用 -Xmx32m 跑这段代码,十秒内进程挂掉
List<byte[]> leak = new ArrayList<>();
while (true) {
leak.add(new byte[1024 * 1024]); // 每轮吃掉 1MB,只进不出
}
// 遗言长这样:java.lang.OutOfMemoryError: Java heap space
// 堆栈里往往还留着案发地点——谁在疯狂分配挂着不可怕,可怕的是看不懂它临终前的遗言。那段 OutOfMemoryError 的堆栈,其实早就把案发地点写在里面了——只是不认识 JVM 的人,把它当成了乱码。
懂 JVM 的三个实际收益
第一,故障定位从"重启续命"变成"十分钟锁定":CPU 飙高、内存泄漏、频繁 Full GC,都有成熟的排查路径,本系列会一篇篇给到。第二,性能优化有了抓手:知道新生代和老年代的分工,才明白缓存为什么不能无上限;理解停顿的来源,才知道该动哪个旋钮。第三,面试不虚——JVM 是后端面试的固定节目,而且面试官最爱追问"你实际排查过什么问题",背概念是糊弄不过去的。
这个系列的路线图
二十篇分五段走:内存与对象(运行时数据区、对象的一生)、类加载(加载五步、双亲委派)、垃圾回收(标记、算法、Serial 到 ZGC)、实战排查(GC 日志、四件套、MAT、OOM 地图、CPU 飙高、内存泄漏)、调优进阶(参数思路、JIT、堆外内存)。
| 阶段 | 回答的问题 | 篇目 |
|---|---|---|
| 内存与对象 | 内存怎么分、对象怎么生 | 2-3 |
| 类加载 | Class 从哪来、怎么装 | 4-5 |
| 垃圾回收 | 谁收、怎么收、谁来收 | 6-10 |
| 实战排查 | 出事了怎么查 | 11-16 |
| 调优进阶 | 怎么让它跑得更好 | 17-20 |
先把环境备好
建议统一用 JDK 17 跟做:语法不是重点,重要的是新收集器的默认行为已经变了。装好后把 IDEA 自带的 Profiler 或 JConsole 摸一遍,系列里每个实验都值得亲手跑一次——JVM 的知识不看会忘,跑过一遍就长在身上了。下一篇从地基讲起:运行时数据区,堆和栈到底怎么分,StackOverflowError 和 OOM 为什么报在不同的地方。
评论 (0)