前言
很多人刚接触 JVM 内存模型时,最先听说的往往是堆区。规范里写得很明白,几乎所有的对象和数组都要分配在堆里。但堆具体长什么样、里面各区域怎么配合,生产配置参数又藏着哪些门道,不少人其实没有深究过。本文就把堆的内部结构与核心参数一次性拆解清楚。
文章目录
-
- 前言
- 一、对象在堆里而引用在栈里
-
- [1.1 方法退栈后对象为什么不立刻消失](#1.1 方法退栈后对象为什么不立刻消失)
- 二、生产环境为什么必须把Xms和Xmx配成一样
-
- [2.1 堆内存的默认扩缩容机制](#2.1 堆内存的默认扩缩容机制)
- [2.2 频繁扩缩容带来的性能抖动与开销](#2.2 频繁扩缩容带来的性能抖动与开销)
- 三、堆内存为什么非要分代
-
- [3.1 绝大多数对象朝生夕灭的统计学规律](#3.1 绝大多数对象朝生夕灭的统计学规律)
- [3.2 新生代与老年代的比例划分规则](#3.2 新生代与老年代的比例划分规则)
- 四、新生代里为什么需要两个幸存者区
-
- [4.1 8比1比1的黄金比例](#4.1 8比1比1的黄金比例)
- [4.2 From与To轮转如何彻底解决内存碎片](#4.2 From与To轮转如何彻底解决内存碎片)
- [4.3 对象从出生到进入老年代的完整流转](#4.3 对象从出生到进入老年代的完整流转)
- 五、写在最后
一、对象在堆里而引用在栈里
写 Java 代码的时候,创建对象就像呼吸一样自然:
java
package com.crayontech.jvm;
public class HeapMemoryDemo {
public void processTask() {
Order order = new Order();
order.execute();
}
}
表面看只有短短两行代码,但在 JVM 底层,它其实跨越了两个完全不同的内存空间。
前面讲执行引擎和虚拟机栈时我们知道,当线程调用 processTask 方法时,JVM 会为它在虚拟机栈里压入一个栈帧。这个栈帧里的局部变量表会分配一个 Slot,用来存放变量 order。
但这个 Slot 里存的并不是真正的订单对象,而仅仅是一个内存引用地址 (Reference)。真正的 Order 实例对象,是在堆(Heap)里面开辟空间存放的。
#mermaid-svg-5Y3FZfVkhPTzWEWM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Y3FZfVkhPTzWEWM .error-icon{fill:#552222;}#mermaid-svg-5Y3FZfVkhPTzWEWM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Y3FZfVkhPTzWEWM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .marker.cross{stroke:#333333;}#mermaid-svg-5Y3FZfVkhPTzWEWM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Y3FZfVkhPTzWEWM p{margin:0;}#mermaid-svg-5Y3FZfVkhPTzWEWM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster-label text{fill:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster-label span{color:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster-label span p{background-color:transparent;}#mermaid-svg-5Y3FZfVkhPTzWEWM .label text,#mermaid-svg-5Y3FZfVkhPTzWEWM span{fill:#333;color:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .node rect,#mermaid-svg-5Y3FZfVkhPTzWEWM .node circle,#mermaid-svg-5Y3FZfVkhPTzWEWM .node ellipse,#mermaid-svg-5Y3FZfVkhPTzWEWM .node polygon,#mermaid-svg-5Y3FZfVkhPTzWEWM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .rough-node .label text,#mermaid-svg-5Y3FZfVkhPTzWEWM .node .label text,#mermaid-svg-5Y3FZfVkhPTzWEWM .image-shape .label,#mermaid-svg-5Y3FZfVkhPTzWEWM .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Y3FZfVkhPTzWEWM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .rough-node .label,#mermaid-svg-5Y3FZfVkhPTzWEWM .node .label,#mermaid-svg-5Y3FZfVkhPTzWEWM .image-shape .label,#mermaid-svg-5Y3FZfVkhPTzWEWM .icon-shape .label{text-align:center;}#mermaid-svg-5Y3FZfVkhPTzWEWM .node.clickable{cursor:pointer;}#mermaid-svg-5Y3FZfVkhPTzWEWM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .arrowheadPath{fill:#333333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Y3FZfVkhPTzWEWM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Y3FZfVkhPTzWEWM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Y3FZfVkhPTzWEWM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster text{fill:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM .cluster span{color:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Y3FZfVkhPTzWEWM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Y3FZfVkhPTzWEWM rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Y3FZfVkhPTzWEWM .icon-shape,#mermaid-svg-5Y3FZfVkhPTzWEWM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Y3FZfVkhPTzWEWM .icon-shape p,#mermaid-svg-5Y3FZfVkhPTzWEWM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Y3FZfVkhPTzWEWM .icon-shape .label rect,#mermaid-svg-5Y3FZfVkhPTzWEWM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Y3FZfVkhPTzWEWM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Y3FZfVkhPTzWEWM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Y3FZfVkhPTzWEWM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程共享:堆内存 (Heap)
线程私有:虚拟机栈
processTask 方法栈帧
持有内存指针
局部变量表: Slot 1 (order 引用地址 0x7A0012)
Order 对象实体 (0x7A0012)
-
对象头 (Mark Word + Klass Word)
-
实例数据 (orderId, amount...)
-
对齐填充
1.1 方法退栈后对象为什么不立刻消失
这里有一个刚学 JVM 时容易混淆的细节:方法执行完了,这个对象会立刻被销毁吗?
答案是不会。
当 processTask 执行完毕,方法对应的栈帧会瞬间出栈并销毁。随着栈顶指针回退,局部变量表里的 order 引用直接失效。但是,堆内存里的那个 Order 对象实体依然安安静静地躺在堆空间里。
因为虚拟机栈只管当前线程的方法调用生命周期,而堆是所有线程共享的公用内存池。JVM 设计者不可能让一个线程在方法退出时,还专门停下来去堆里把刚才用过的对象立刻抹除。真要这么做,方法调用的开销就太大了。
堆中的对象什么时候清理,完全由专门的**垃圾收集器(Garbage Collector,简称 GC)**在后台异步扫描并决定。只要对象不再被任何存活的引用关联,它就会在后续的 GC 周期里被回收释放。
二、生产环境为什么必须把Xms和Xmx配成一样
既然堆要承载系统中绝大部分对象,那这块空间到底有多大?
JVM 提供了两个最基础的堆内存配置参数:
-Xms:全称是 Memory Start,表示堆的初始分配内存大小 ,等价于-XX:InitialHeapSize。-Xmx:全称是 Memory Max,表示堆所允许使用的最大内存上限 ,等价于-XX:MaxHeapSize。
如果不做任何手动配置,JVM 在启动时会根据宿主机的物理内存自动推算默认值:初始内存通常是宿主机物理内存的 1/64 左右,最大内存通常是物理内存的 1/4 左右。
2.1 堆内存的默认扩缩容机制
在未显式把两个参数设为相等的情况下,堆内存是具有弹性的。
JVM 启动时,只会先向操作系统申请 -Xms 指定的内存。随着业务运行,新的对象源源不断被创建,一旦当前的堆空间不够用了,JVM 就会触发 GC。如果 GC 之后可用空间依然吃紧,JVM 就会主动向操作系统申请扩展内存,直到达到 -Xmx 规定的上限。
反过来,如果经过某次大范围垃圾回收后,堆里的空闲率过高,JVM 还会尝试把一部分内存退还给操作系统。
听起来这个动态弹性机制非常智能,既不浪费宿主机内存,又能按需索取。但在高并发的生产环境中,这种设计往往是性能杀手。
#mermaid-svg-voeJWkjGrl4pOSMl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-voeJWkjGrl4pOSMl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-voeJWkjGrl4pOSMl .error-icon{fill:#552222;}#mermaid-svg-voeJWkjGrl4pOSMl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-voeJWkjGrl4pOSMl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-voeJWkjGrl4pOSMl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-voeJWkjGrl4pOSMl .marker.cross{stroke:#333333;}#mermaid-svg-voeJWkjGrl4pOSMl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-voeJWkjGrl4pOSMl p{margin:0;}#mermaid-svg-voeJWkjGrl4pOSMl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-voeJWkjGrl4pOSMl .cluster-label text{fill:#333;}#mermaid-svg-voeJWkjGrl4pOSMl .cluster-label span{color:#333;}#mermaid-svg-voeJWkjGrl4pOSMl .cluster-label span p{background-color:transparent;}#mermaid-svg-voeJWkjGrl4pOSMl .label text,#mermaid-svg-voeJWkjGrl4pOSMl span{fill:#333;color:#333;}#mermaid-svg-voeJWkjGrl4pOSMl .node rect,#mermaid-svg-voeJWkjGrl4pOSMl .node circle,#mermaid-svg-voeJWkjGrl4pOSMl .node ellipse,#mermaid-svg-voeJWkjGrl4pOSMl .node polygon,#mermaid-svg-voeJWkjGrl4pOSMl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-voeJWkjGrl4pOSMl .rough-node .label text,#mermaid-svg-voeJWkjGrl4pOSMl .node .label text,#mermaid-svg-voeJWkjGrl4pOSMl .image-shape .label,#mermaid-svg-voeJWkjGrl4pOSMl .icon-shape .label{text-anchor:middle;}#mermaid-svg-voeJWkjGrl4pOSMl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-voeJWkjGrl4pOSMl .rough-node .label,#mermaid-svg-voeJWkjGrl4pOSMl .node .label,#mermaid-svg-voeJWkjGrl4pOSMl .image-shape .label,#mermaid-svg-voeJWkjGrl4pOSMl .icon-shape .label{text-align:center;}#mermaid-svg-voeJWkjGrl4pOSMl .node.clickable{cursor:pointer;}#mermaid-svg-voeJWkjGrl4pOSMl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-voeJWkjGrl4pOSMl .arrowheadPath{fill:#333333;}#mermaid-svg-voeJWkjGrl4pOSMl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-voeJWkjGrl4pOSMl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-voeJWkjGrl4pOSMl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-voeJWkjGrl4pOSMl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-voeJWkjGrl4pOSMl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-voeJWkjGrl4pOSMl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-voeJWkjGrl4pOSMl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-voeJWkjGrl4pOSMl .cluster text{fill:#333;}#mermaid-svg-voeJWkjGrl4pOSMl .cluster span{color:#333;}#mermaid-svg-voeJWkjGrl4pOSMl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-voeJWkjGrl4pOSMl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-voeJWkjGrl4pOSMl rect.text{fill:none;stroke-width:0;}#mermaid-svg-voeJWkjGrl4pOSMl .icon-shape,#mermaid-svg-voeJWkjGrl4pOSMl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-voeJWkjGrl4pOSMl .icon-shape p,#mermaid-svg-voeJWkjGrl4pOSMl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-voeJWkjGrl4pOSMl .icon-shape .label rect,#mermaid-svg-voeJWkjGrl4pOSMl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-voeJWkjGrl4pOSMl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-voeJWkjGrl4pOSMl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-voeJWkjGrl4pOSMl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产推荐锁定堆 (-Xms == -Xmx)
业务请求激增
内存早已一次性分配就绪
零扩容系统调用开销
平稳运行,资源全力供给业务代码
动态伸缩堆 (-Xms < -Xmx)
业务请求激增
堆空间不足
触发垃圾回收与停顿
向操作系统申请扩容系统调用
发生内存抖动与接口响应毛刺
2.2 频繁扩缩容带来的性能抖动与开销
我一直建议:在正式的生产环境中,必须无条件把 -Xms 和 -Xmx 设置为完全相同的数值。
原因其实非常纯粹:
向操作系统申请内存和归还内存,从来都不是免费的午餐。每次堆内存扩容,JVM 都需要发起底层的系统调用,重新划分内存页表与地址空间。伴随这个过程的,往往是更频繁的 Full GC 和更漫长的心跳停顿。
如果一个 Java 服务的堆在 2GB 到 8GB 之间反复横跳,不仅 CPU 资源会被频繁的内存伸缩和 GC 标记挤占,还会让外部监控观察到严重的请求延迟毛刺。
把 -Xms 和 -Xmx 设为一样大(比如同时配成 -Xms8g -Xmx8g),JVM 在进程启动那一刻就会向操作系统一次性锁定这 8GB 的物理连续空间。之后整个运行期内,堆大小固定不变,彻底杜绝了动态调整内存带来的额外消耗。
当然,如果对象分配速度彻底失控,把整个 -Xmx 撑爆了且 GC 回天乏术,JVM 就会毫不留情地抛出我们熟悉的堆溢出错误:
text
java.lang.OutOfMemoryError: Java heap space
三、堆内存为什么非要分代
看清楚了堆的大小控制,我们再来看堆内部的构造。
如果让你来设计堆内存,最简单的方案莫过于把整块堆当成一个大蓄水池:对象来了就往里填,内存满了就做一次全盘大扫除。
但 JVM 团队没有这么做。在主流的 HotSpot 虚拟机里,堆被严格切分成了新生代(Young Generation)和老年代(Old Generation)。
为什么要搞得这么麻烦?
#mermaid-svg-5w2zqONcZKY2sJqo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5w2zqONcZKY2sJqo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5w2zqONcZKY2sJqo .error-icon{fill:#552222;}#mermaid-svg-5w2zqONcZKY2sJqo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5w2zqONcZKY2sJqo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5w2zqONcZKY2sJqo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5w2zqONcZKY2sJqo .marker.cross{stroke:#333333;}#mermaid-svg-5w2zqONcZKY2sJqo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5w2zqONcZKY2sJqo p{margin:0;}#mermaid-svg-5w2zqONcZKY2sJqo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5w2zqONcZKY2sJqo .cluster-label text{fill:#333;}#mermaid-svg-5w2zqONcZKY2sJqo .cluster-label span{color:#333;}#mermaid-svg-5w2zqONcZKY2sJqo .cluster-label span p{background-color:transparent;}#mermaid-svg-5w2zqONcZKY2sJqo .label text,#mermaid-svg-5w2zqONcZKY2sJqo span{fill:#333;color:#333;}#mermaid-svg-5w2zqONcZKY2sJqo .node rect,#mermaid-svg-5w2zqONcZKY2sJqo .node circle,#mermaid-svg-5w2zqONcZKY2sJqo .node ellipse,#mermaid-svg-5w2zqONcZKY2sJqo .node polygon,#mermaid-svg-5w2zqONcZKY2sJqo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5w2zqONcZKY2sJqo .rough-node .label text,#mermaid-svg-5w2zqONcZKY2sJqo .node .label text,#mermaid-svg-5w2zqONcZKY2sJqo .image-shape .label,#mermaid-svg-5w2zqONcZKY2sJqo .icon-shape .label{text-anchor:middle;}#mermaid-svg-5w2zqONcZKY2sJqo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5w2zqONcZKY2sJqo .rough-node .label,#mermaid-svg-5w2zqONcZKY2sJqo .node .label,#mermaid-svg-5w2zqONcZKY2sJqo .image-shape .label,#mermaid-svg-5w2zqONcZKY2sJqo .icon-shape .label{text-align:center;}#mermaid-svg-5w2zqONcZKY2sJqo .node.clickable{cursor:pointer;}#mermaid-svg-5w2zqONcZKY2sJqo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5w2zqONcZKY2sJqo .arrowheadPath{fill:#333333;}#mermaid-svg-5w2zqONcZKY2sJqo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5w2zqONcZKY2sJqo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5w2zqONcZKY2sJqo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5w2zqONcZKY2sJqo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5w2zqONcZKY2sJqo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5w2zqONcZKY2sJqo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5w2zqONcZKY2sJqo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5w2zqONcZKY2sJqo .cluster text{fill:#333;}#mermaid-svg-5w2zqONcZKY2sJqo .cluster span{color:#333;}#mermaid-svg-5w2zqONcZKY2sJqo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5w2zqONcZKY2sJqo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5w2zqONcZKY2sJqo rect.text{fill:none;stroke-width:0;}#mermaid-svg-5w2zqONcZKY2sJqo .icon-shape,#mermaid-svg-5w2zqONcZKY2sJqo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5w2zqONcZKY2sJqo .icon-shape p,#mermaid-svg-5w2zqONcZKY2sJqo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5w2zqONcZKY2sJqo .icon-shape .label rect,#mermaid-svg-5w2zqONcZKY2sJqo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5w2zqONcZKY2sJqo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5w2zqONcZKY2sJqo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5w2zqONcZKY2sJqo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 堆内存划分 (默认比例 1 : 2)
新生代 (Young Gen)
占比: 1/3
存放朝生夕灭的新生对象
老年代 (Old Gen)
占比: 2/3
存放长期存活与大对象
3.1 绝大多数对象朝生夕灭的统计学规律
分代设计的理论基石,是著名的弱分代假说(Weak Generational Hypothesis)。
工程师们经过大量实际工程统计发现:在运行的 Java 系统中,超过 90% 的对象都是朝生夕灭的。
想想平时写的方法:
java
package com.crayontech.jvm;
import java.util.HashMap;
import java.util.Map;
public class TemporaryObjectDemo {
public String buildJsonResponse(long id, String name) {
// map 和内部的 key、value 对象仅在当前方法内部存活
Map<String, Object> data = new HashMap<>();
data.put("id", id);
data.put("name", name);
return data.toString();
}
}
每次调用 buildJsonResponse,方法里创建的 HashMap 实例只用来做数据转换。一旦方法返回,这些临时对象在下一毫秒就彻底失去了被引用的价值。
只有极少数对象能够长命百岁:比如 Spring 容器里的单例 Bean、全局数据库连接池、系统常驻缓存。
如果把朝生夕灭的临时工和长命百岁的老员工混在一起存,每次垃圾回收就必须把几十个 G 的整个堆内存全部扫描一遍。这种无差别全量扫描对系统吞吐量来说是灾难性的。
分代之后,GC 的思路立刻清晰了:
- 对新生代采用高频、快速的局部回收(Minor GC / Young GC),专门清理那些刚生下来就死掉的短命鬼;
- 对老年代采用低频稳健的回收策略(Major GC / Full GC),不用每次都去打扰那些长期存活的老对象。
3.2 新生代与老年代的比例划分规则
既然分了新生代和老年代,那各自该占多大地盘?
JVM 提供了 -XX:NewRatio 参数来配置老年代与新生代的比例,默认值是 2。
这里要看清比例的方向:
NewRatio=2表示的是 老年代 : 新生代 = 2 : 1。- 换句话说,新生代占据了整个堆空间的 1/3 ,而老年代占据了整个堆空间的 2/3。
老年代的地盘明显比新生代大得多。这也是符合工程常识的:老年代里积攒的都是历经多次 GC 依然幸存的硬骨头,而且没有备用缓冲区,必须预留足够的空间作为安全水位,防止突发流量直接冲垮老年代。
四、新生代里为什么需要两个幸存者区
把目光聚焦到新生代内部,你会发现设计者在这里玩出了更精妙的花样。
新生代内部细分成了三个部分:
- Eden 区(伊甸园区):新对象降生的地方;
- From Survivor 区(S0,幸存者 0 区);
- To Survivor 区(S1,幸存者 1 区)。
#mermaid-svg-Yyw5Tq7tpXVavdoA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yyw5Tq7tpXVavdoA .error-icon{fill:#552222;}#mermaid-svg-Yyw5Tq7tpXVavdoA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yyw5Tq7tpXVavdoA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .marker.cross{stroke:#333333;}#mermaid-svg-Yyw5Tq7tpXVavdoA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yyw5Tq7tpXVavdoA p{margin:0;}#mermaid-svg-Yyw5Tq7tpXVavdoA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster-label text{fill:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster-label span{color:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster-label span p{background-color:transparent;}#mermaid-svg-Yyw5Tq7tpXVavdoA .label text,#mermaid-svg-Yyw5Tq7tpXVavdoA span{fill:#333;color:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .node rect,#mermaid-svg-Yyw5Tq7tpXVavdoA .node circle,#mermaid-svg-Yyw5Tq7tpXVavdoA .node ellipse,#mermaid-svg-Yyw5Tq7tpXVavdoA .node polygon,#mermaid-svg-Yyw5Tq7tpXVavdoA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .rough-node .label text,#mermaid-svg-Yyw5Tq7tpXVavdoA .node .label text,#mermaid-svg-Yyw5Tq7tpXVavdoA .image-shape .label,#mermaid-svg-Yyw5Tq7tpXVavdoA .icon-shape .label{text-anchor:middle;}#mermaid-svg-Yyw5Tq7tpXVavdoA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .rough-node .label,#mermaid-svg-Yyw5Tq7tpXVavdoA .node .label,#mermaid-svg-Yyw5Tq7tpXVavdoA .image-shape .label,#mermaid-svg-Yyw5Tq7tpXVavdoA .icon-shape .label{text-align:center;}#mermaid-svg-Yyw5Tq7tpXVavdoA .node.clickable{cursor:pointer;}#mermaid-svg-Yyw5Tq7tpXVavdoA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .arrowheadPath{fill:#333333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yyw5Tq7tpXVavdoA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Yyw5Tq7tpXVavdoA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yyw5Tq7tpXVavdoA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster text{fill:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA .cluster span{color:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Yyw5Tq7tpXVavdoA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Yyw5Tq7tpXVavdoA rect.text{fill:none;stroke-width:0;}#mermaid-svg-Yyw5Tq7tpXVavdoA .icon-shape,#mermaid-svg-Yyw5Tq7tpXVavdoA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yyw5Tq7tpXVavdoA .icon-shape p,#mermaid-svg-Yyw5Tq7tpXVavdoA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Yyw5Tq7tpXVavdoA .icon-shape .label rect,#mermaid-svg-Yyw5Tq7tpXVavdoA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yyw5Tq7tpXVavdoA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Yyw5Tq7tpXVavdoA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Yyw5Tq7tpXVavdoA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新生代内部结构 (8 : 1 : 1)
Eden 伊甸园区 (80%)
绝大部分新对象诞生于此
Survivor 0 / From (10%)
存放存活下来的幸存对象
Survivor 1 / To (10%)
GC 时作为空的接收容器
4.1 8比1比1的黄金比例
新生代各个区域的大小比例由 -XX:SurvivorRatio 控制,默认参数是 8。
这个配置意味着:
- Eden : Survivor 0 : Survivor 1 = 8 : 1 : 1。
- Eden 区独占新生代总容量的 80%;
- 两个 Survivor 区各占新生代总容量的 10%。
让 Eden 独占八成空间,同样是顺应了前面提到的统计学规律:既然绝大多数对象很快就会消亡,那就给"生产车间"留出最大面积,让对象尽管创建。
4.2 From与To轮转如何彻底解决内存碎片
很多同学最大的困惑在于:既然叫幸存者区,留一个不就行了吗?为什么一定要设计成两个 Survivor 区(S0 和 S1)?
答案只有四个字:内存碎片。
假设只有一个 Survivor 区,看看会发生什么情况:
Eden 空间满了,触发 Minor GC,把活下来的少量对象搬运到唯一的那块 Survivor 区。接着系统继续运行,Eden 又满了,Survivor 里的对象也有一部分死掉了。
此时如果你想清理 Survivor,你就得在原地把死掉的对象挖掉。可这样一来,Survivor 内存里就会留下密密麻麻的空洞和碎片。当下一次再有稍微大一点的存活对象想塞进来时,就会因为找不到连续的物理空间而提前报错。
为了彻底消除碎片,JVM 在新生代采用了经典的标记-复制算法(Copying Algorithm)。
两个 Survivor 区轮流扮演角色,一个当 From ,一个当 To:
- 核心规矩 :无论何时,To 区必须时刻保持完全空闲。
- 当 Eden 区满了触发 Minor GC 时,垃圾回收器会把 Eden 区里依然存活的对象,连同当前那个 From 区里依然存活的对象,一口气整整齐齐地复制到空的 To 区里。
- 复制完成后,Eden 区和原先的 From 区瞬间被整体清空,变成一张白纸。
- 最后,刚才装满对象的 To 区摇身一变成为新的 From 区,原先清空的 From 区则变成下一次 GC 专用的 To 区。
To Survivor (S1,初始为空) From Survivor (S0) Eden 区 (80%) To Survivor (S1,初始为空) From Survivor (S0) Eden 区 (80%) #mermaid-svg-L79PRXG6ZfUZjRTO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-L79PRXG6ZfUZjRTO .error-icon{fill:#552222;}#mermaid-svg-L79PRXG6ZfUZjRTO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L79PRXG6ZfUZjRTO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L79PRXG6ZfUZjRTO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L79PRXG6ZfUZjRTO .marker.cross{stroke:#333333;}#mermaid-svg-L79PRXG6ZfUZjRTO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L79PRXG6ZfUZjRTO p{margin:0;}#mermaid-svg-L79PRXG6ZfUZjRTO .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L79PRXG6ZfUZjRTO text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-L79PRXG6ZfUZjRTO .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-L79PRXG6ZfUZjRTO .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-L79PRXG6ZfUZjRTO #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-L79PRXG6ZfUZjRTO .sequenceNumber{fill:white;}#mermaid-svg-L79PRXG6ZfUZjRTO #sequencenumber{fill:#333;}#mermaid-svg-L79PRXG6ZfUZjRTO #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-L79PRXG6ZfUZjRTO .messageText{fill:#333;stroke:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L79PRXG6ZfUZjRTO .labelText,#mermaid-svg-L79PRXG6ZfUZjRTO .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .loopText,#mermaid-svg-L79PRXG6ZfUZjRTO .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-L79PRXG6ZfUZjRTO .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-L79PRXG6ZfUZjRTO .noteText,#mermaid-svg-L79PRXG6ZfUZjRTO .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-L79PRXG6ZfUZjRTO .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L79PRXG6ZfUZjRTO .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L79PRXG6ZfUZjRTO .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L79PRXG6ZfUZjRTO .actorPopupMenu{position:absolute;}#mermaid-svg-L79PRXG6ZfUZjRTO .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-L79PRXG6ZfUZjRTO .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L79PRXG6ZfUZjRTO .actor-man circle,#mermaid-svg-L79PRXG6ZfUZjRTO line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-L79PRXG6ZfUZjRTO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 触发 Minor GC 前:Eden 放满,S0 有上一轮存活对象 彻底清空 Eden 与 From 角色互换:S1 变成新 From,S0 变成新 To 复制 Eden 中存活对象(紧凑排列) 1 复制 From 中仍存活对象(年龄+1) 2 重置为空白内存 3 重置为空白内存 4
通过这套犹如杂耍般的轮转交替,新生代不仅清理速度极快,而且每次复制过去的对象都是首尾相连紧凑排列的,连一个字节的内存碎片都不会产生。
4.3 对象从出生到进入老年代的完整流转
把上面讲的所有区域串联起来,一个 Java 对象从创建到晋升老年代的完整生命历程是这样的:
- 出生降临 :绝大多数对象在
new出来后,首先被分配在 Eden 区。 - 初次大浪淘沙:当 Eden 区快满时,触发一次 Minor GC。绝大部分短命对象直接在 Eden 里阵亡;少数命硬活下来的,被打包复制到 To 区,对象的年龄计数器(Age)被标记为 1。
- Survivor 之间倒腾:之后系统继续运行,每经历一次 Minor GC,存活对象的年龄就会递增 1 岁。
- 晋升老年代 :当一个对象的年龄增长到阈值(默认是 15 岁,通过
-XX:MaxTenuringThreshold设定)时,JVM 就会认为它是个长命对象,正式把它晋升(Promote)到老年代常驻。 - 大对象特权通道 :如果一个对象体积极其庞大(比如分配了一个巨大的长字节数组),如果让它在 Eden 和两个 Survivor 之间来回倒腾复制,性能损耗会非常惊人。JVM 允许通过
-XX:PretenureSizeThreshold参数设定阈值,一旦对象大小超过这个值,会直接跳过新生代,直接在老年代开辟空间。
#mermaid-svg-Palc4azz2RSvKe9b{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Palc4azz2RSvKe9b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Palc4azz2RSvKe9b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Palc4azz2RSvKe9b .error-icon{fill:#552222;}#mermaid-svg-Palc4azz2RSvKe9b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Palc4azz2RSvKe9b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Palc4azz2RSvKe9b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Palc4azz2RSvKe9b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Palc4azz2RSvKe9b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Palc4azz2RSvKe9b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Palc4azz2RSvKe9b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Palc4azz2RSvKe9b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Palc4azz2RSvKe9b .marker.cross{stroke:#333333;}#mermaid-svg-Palc4azz2RSvKe9b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Palc4azz2RSvKe9b p{margin:0;}#mermaid-svg-Palc4azz2RSvKe9b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Palc4azz2RSvKe9b .cluster-label text{fill:#333;}#mermaid-svg-Palc4azz2RSvKe9b .cluster-label span{color:#333;}#mermaid-svg-Palc4azz2RSvKe9b .cluster-label span p{background-color:transparent;}#mermaid-svg-Palc4azz2RSvKe9b .label text,#mermaid-svg-Palc4azz2RSvKe9b span{fill:#333;color:#333;}#mermaid-svg-Palc4azz2RSvKe9b .node rect,#mermaid-svg-Palc4azz2RSvKe9b .node circle,#mermaid-svg-Palc4azz2RSvKe9b .node ellipse,#mermaid-svg-Palc4azz2RSvKe9b .node polygon,#mermaid-svg-Palc4azz2RSvKe9b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Palc4azz2RSvKe9b .rough-node .label text,#mermaid-svg-Palc4azz2RSvKe9b .node .label text,#mermaid-svg-Palc4azz2RSvKe9b .image-shape .label,#mermaid-svg-Palc4azz2RSvKe9b .icon-shape .label{text-anchor:middle;}#mermaid-svg-Palc4azz2RSvKe9b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Palc4azz2RSvKe9b .rough-node .label,#mermaid-svg-Palc4azz2RSvKe9b .node .label,#mermaid-svg-Palc4azz2RSvKe9b .image-shape .label,#mermaid-svg-Palc4azz2RSvKe9b .icon-shape .label{text-align:center;}#mermaid-svg-Palc4azz2RSvKe9b .node.clickable{cursor:pointer;}#mermaid-svg-Palc4azz2RSvKe9b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Palc4azz2RSvKe9b .arrowheadPath{fill:#333333;}#mermaid-svg-Palc4azz2RSvKe9b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Palc4azz2RSvKe9b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Palc4azz2RSvKe9b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Palc4azz2RSvKe9b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Palc4azz2RSvKe9b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Palc4azz2RSvKe9b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Palc4azz2RSvKe9b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Palc4azz2RSvKe9b .cluster text{fill:#333;}#mermaid-svg-Palc4azz2RSvKe9b .cluster span{color:#333;}#mermaid-svg-Palc4azz2RSvKe9b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Palc4azz2RSvKe9b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Palc4azz2RSvKe9b rect.text{fill:none;stroke-width:0;}#mermaid-svg-Palc4azz2RSvKe9b .icon-shape,#mermaid-svg-Palc4azz2RSvKe9b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Palc4azz2RSvKe9b .icon-shape p,#mermaid-svg-Palc4azz2RSvKe9b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Palc4azz2RSvKe9b .icon-shape .label rect,#mermaid-svg-Palc4azz2RSvKe9b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Palc4azz2RSvKe9b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Palc4azz2RSvKe9b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Palc4azz2RSvKe9b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
否
是
是
否
new 创建对象
对象是否超过大对象阈值?
直接在老年代 (Old Gen) 分配
在 Eden 区分配
Eden 空间是否已满?
正常运行
触发 Minor GC (Young GC)
对象是否仍然存活?
直接回收释放
复制到 To Survivor 区,年龄 +1
年龄是否达到 15 岁阈值?
晋升到老年代 (Old Gen)
留在 Survivor 区等待下一次轮转
五、写在最后
回过头来看 JVM 堆的结构设计,它其实是计算机科学中"用空间换时间、按统计学概率做特化优化"的典范:
它没有把内存当成均匀的一整块铁板,而是敏锐地捕捉到了绝大多数对象活不过几次方法调用的客观规律。通过新生代与老年代的二八分化,再配合 Survivor 区的 From/To 轮转复制,JVM 用极小的 GC 停顿代价,换取了支撑超高并发吞吐量的底气。
而在实际线上部署时,主动把 -Xms 和 -Xmx 锁死为相同大小,更是每一位后端开发都应当刻进骨子里的工程自觉。
如果你觉得本文把堆的结构分工与配置细节讲透了,记得点个关注。后续文章我们会继续顺着这套内存架构,深入拆解对象在内存里的真实排布、指针压缩与垃圾回收器的并发标记细节。