2026-10-31·von Sijie Wang

JVM Memory Model and GC

JVM 内存模型与 GC:内存归谁管,死了怎么收

动机。 Java 的内存不像 C 那样手动管,也不像 Go 那样编译器加运行时一把梭,JVM 替你管分配和回收。但替你管不等于不用懂,OOM、GC 停顿、内存泄漏查起来,全靠这张图。

运行时数据区,分线程私有和共享两半。 线程私有的,每条线程一份:程序计数器(当前字节码行号,唯一一个不抛 OutOfMemoryError 的区)、虚拟机栈(一个方法一个栈帧,帧里是局部变量表加操作数栈)、本地方法栈(native 方法用的,跟虚拟机栈一个意思)。线程共享的,所有线程共用:堆(对象实例和数组都在这儿,分代,下面讲)、方法区(类元数据、常量池;JDK 8 之后叫元空间,用本地内存,不在堆里了,老的永久代 PermGen 被移除了)。

GC 的核心就两问:哪些死,死了怎么收。 哪些死用可达性分析:从 GC Roots 出发,能顺着引用链找到的就是活的,找不到的就是死的。GC Roots 包括栈帧里的局部变量、静态字段、常量引用、JNI 引用这几类。

死了怎么收靠分代假说:绝大多数对象朝生夕死,活得久的越活越久。所以堆分年轻代和老年代。年轻代是 Eden 加两个 Survivor(from 和 to),新对象先进 Eden,Eden 满了做一次 Minor GC,活下来的在两个 Survivor 之间复制来回倒,年龄够了(默认 15 岁,也有动态年龄判定)就晋升老年代。老年代满了做 Major GC,回收算法是标记清除或者标记整理。回收的时候要 STW(Stop-The-World,全拼就是“停止世界”,所有应用线程暂停),停顿时间就是这么来的。

收集器只列一张地图。 Serial(单线程,服务端今天基本不用)、Parallel(吞吐量优先,JDK 8 默认)、G1(JDK 9 之后默认:堆切成很多分区、混合回收)、ZGC 和 Shenandoah(停顿优先:着色指针加读屏障,毫秒级停顿)。跟 Parallel 的 trade-off 是:Parallel 只求总 GC 时间最短,不管单次停顿多长;G1、ZGC 用更多的总 CPU(并发标记、屏障、分区管理)换更短的单次停顿。所以批处理、离线任务这种停顿不敏感的场景,Parallel 天生就赢;G1 在疏散失败时会回退 Full GC(堆太小、晋升太快、大对象多),ZGC 要求堆有 headroom(并发回收要跑赢分配),分代 ZGC 之前的版本(分代是 JDK 21 才来的)对短命对象多的场景要全堆并发标记。默认参数也会带偏直接对比:G1 默认 200ms 停顿目标(MaxGCPauseMillis)让它频繁做小回收、牺牲吞吐,Parallel 默认不设停顿目标。

跟 Go GC 对一下。 Go 是一套算法走天下:三色标记加写屏障、两端 STW;JVM 是分代加多收集器可选,同一问题两种答案。

über diesen eintrag

Einer von sijie's Wiki-Einträgen. Die KI auf dieser Site ist im selben Corpus verankert und antwortet in sijie's Stimme mit Zitaten zurück auf Einträge wie diesen — das Beantworten kostet sijie Geld, daher wartet sie hinter einem Code: zugriffscode eingeben →