JVM 运行时数据区:程序计数器、堆、虚拟机栈与栈帧

JVM 内存结构是 JVM 面试里最基础的一组问题。它不只是让你背"堆、栈、方法区",而是在考你能不能说清楚:一段 Java 代码运行时,线程执行到哪里、方法怎么调用、对象放在哪里、哪些区域会发生内存溢出。

先看整体结构:
#mermaid-svg-4hLJbUobYKG0tM7h{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-4hLJbUobYKG0tM7h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4hLJbUobYKG0tM7h .error-icon{fill:#552222;}#mermaid-svg-4hLJbUobYKG0tM7h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4hLJbUobYKG0tM7h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4hLJbUobYKG0tM7h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4hLJbUobYKG0tM7h .marker.cross{stroke:#333333;}#mermaid-svg-4hLJbUobYKG0tM7h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4hLJbUobYKG0tM7h p{margin:0;}#mermaid-svg-4hLJbUobYKG0tM7h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4hLJbUobYKG0tM7h .cluster-label text{fill:#333;}#mermaid-svg-4hLJbUobYKG0tM7h .cluster-label span{color:#333;}#mermaid-svg-4hLJbUobYKG0tM7h .cluster-label span p{background-color:transparent;}#mermaid-svg-4hLJbUobYKG0tM7h .label text,#mermaid-svg-4hLJbUobYKG0tM7h span{fill:#333;color:#333;}#mermaid-svg-4hLJbUobYKG0tM7h .node rect,#mermaid-svg-4hLJbUobYKG0tM7h .node circle,#mermaid-svg-4hLJbUobYKG0tM7h .node ellipse,#mermaid-svg-4hLJbUobYKG0tM7h .node polygon,#mermaid-svg-4hLJbUobYKG0tM7h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4hLJbUobYKG0tM7h .rough-node .label text,#mermaid-svg-4hLJbUobYKG0tM7h .node .label text,#mermaid-svg-4hLJbUobYKG0tM7h .image-shape .label,#mermaid-svg-4hLJbUobYKG0tM7h .icon-shape .label{text-anchor:middle;}#mermaid-svg-4hLJbUobYKG0tM7h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4hLJbUobYKG0tM7h .rough-node .label,#mermaid-svg-4hLJbUobYKG0tM7h .node .label,#mermaid-svg-4hLJbUobYKG0tM7h .image-shape .label,#mermaid-svg-4hLJbUobYKG0tM7h .icon-shape .label{text-align:center;}#mermaid-svg-4hLJbUobYKG0tM7h .node.clickable{cursor:pointer;}#mermaid-svg-4hLJbUobYKG0tM7h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4hLJbUobYKG0tM7h .arrowheadPath{fill:#333333;}#mermaid-svg-4hLJbUobYKG0tM7h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4hLJbUobYKG0tM7h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4hLJbUobYKG0tM7h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4hLJbUobYKG0tM7h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4hLJbUobYKG0tM7h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4hLJbUobYKG0tM7h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4hLJbUobYKG0tM7h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4hLJbUobYKG0tM7h .cluster text{fill:#333;}#mermaid-svg-4hLJbUobYKG0tM7h .cluster span{color:#333;}#mermaid-svg-4hLJbUobYKG0tM7h 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-4hLJbUobYKG0tM7h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4hLJbUobYKG0tM7h rect.text{fill:none;stroke-width:0;}#mermaid-svg-4hLJbUobYKG0tM7h .icon-shape,#mermaid-svg-4hLJbUobYKG0tM7h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4hLJbUobYKG0tM7h .icon-shape p,#mermaid-svg-4hLJbUobYKG0tM7h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4hLJbUobYKG0tM7h .icon-shape .label rect,#mermaid-svg-4hLJbUobYKG0tM7h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4hLJbUobYKG0tM7h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4hLJbUobYKG0tM7h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4hLJbUobYKG0tM7h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JVM 进程可使用的本地内存
线程共享区域

方法区
线程私有区域
程序计数器
虚拟机栈
本地方法栈
直接内存
B

其中,程序计数器、虚拟机栈、本地方法栈是线程私有的;堆和方法区是线程共享的。这个"私有"和"共享"的区别非常重要,它会影响线程安全、垃圾回收和内存溢出的判断。

程序计数器

程序计数器是一块很小的线程私有内存,用来记录当前线程正在执行的字节码指令地址。

为什么它必须是线程私有的?

因为 Java 是多线程执行的,CPU 会在线程之间不断切换。线程被切走后,下一次恢复执行时,必须知道自己上次执行到哪一条指令。程序计数器就是用来保存这个位置的。
"线程 2" "CPU" "线程 1" "线程 2" "CPU" "线程 1" #mermaid-svg-bp88tB2SWCdNCB85{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-bp88tB2SWCdNCB85 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bp88tB2SWCdNCB85 .error-icon{fill:#552222;}#mermaid-svg-bp88tB2SWCdNCB85 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bp88tB2SWCdNCB85 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bp88tB2SWCdNCB85 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bp88tB2SWCdNCB85 .marker.cross{stroke:#333333;}#mermaid-svg-bp88tB2SWCdNCB85 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bp88tB2SWCdNCB85 p{margin:0;}#mermaid-svg-bp88tB2SWCdNCB85 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bp88tB2SWCdNCB85 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-bp88tB2SWCdNCB85 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-bp88tB2SWCdNCB85 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-bp88tB2SWCdNCB85 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-bp88tB2SWCdNCB85 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-bp88tB2SWCdNCB85 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-bp88tB2SWCdNCB85 .sequenceNumber{fill:white;}#mermaid-svg-bp88tB2SWCdNCB85 #sequencenumber{fill:#333;}#mermaid-svg-bp88tB2SWCdNCB85 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-bp88tB2SWCdNCB85 .messageText{fill:#333;stroke:none;}#mermaid-svg-bp88tB2SWCdNCB85 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bp88tB2SWCdNCB85 .labelText,#mermaid-svg-bp88tB2SWCdNCB85 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-bp88tB2SWCdNCB85 .loopText,#mermaid-svg-bp88tB2SWCdNCB85 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-bp88tB2SWCdNCB85 .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-bp88tB2SWCdNCB85 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-bp88tB2SWCdNCB85 .noteText,#mermaid-svg-bp88tB2SWCdNCB85 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-bp88tB2SWCdNCB85 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bp88tB2SWCdNCB85 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bp88tB2SWCdNCB85 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bp88tB2SWCdNCB85 .actorPopupMenu{position:absolute;}#mermaid-svg-bp88tB2SWCdNCB85 .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-bp88tB2SWCdNCB85 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bp88tB2SWCdNCB85 .actor-man circle,#mermaid-svg-bp88tB2SWCdNCB85 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-bp88tB2SWCdNCB85 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} "执行到字节码第 20 行""保存程序计数器位置""切换执行线程 2""保存线程 2 的执行位置""恢复执行""从第 20 行继续"

程序计数器有两个特点:

特点 说明
线程私有 每个线程都有自己的执行位置
几乎不会 OOM 它只保存当前线程执行位置,空间很小

如果线程正在执行 Java 方法,程序计数器保存的是字节码指令地址;如果线程正在执行 Native 方法,程序计数器的值可能为空。

Java 堆

堆是 JVM 中最大的一块内存,也是垃圾回收最关注的区域。大多数对象实例和数组都分配在堆中。
#mermaid-svg-dLrx4Zngk4o5rqGp{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-dLrx4Zngk4o5rqGp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dLrx4Zngk4o5rqGp .error-icon{fill:#552222;}#mermaid-svg-dLrx4Zngk4o5rqGp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dLrx4Zngk4o5rqGp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dLrx4Zngk4o5rqGp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dLrx4Zngk4o5rqGp .marker.cross{stroke:#333333;}#mermaid-svg-dLrx4Zngk4o5rqGp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dLrx4Zngk4o5rqGp p{margin:0;}#mermaid-svg-dLrx4Zngk4o5rqGp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster-label text{fill:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster-label span{color:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster-label span p{background-color:transparent;}#mermaid-svg-dLrx4Zngk4o5rqGp .label text,#mermaid-svg-dLrx4Zngk4o5rqGp span{fill:#333;color:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp .node rect,#mermaid-svg-dLrx4Zngk4o5rqGp .node circle,#mermaid-svg-dLrx4Zngk4o5rqGp .node ellipse,#mermaid-svg-dLrx4Zngk4o5rqGp .node polygon,#mermaid-svg-dLrx4Zngk4o5rqGp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dLrx4Zngk4o5rqGp .rough-node .label text,#mermaid-svg-dLrx4Zngk4o5rqGp .node .label text,#mermaid-svg-dLrx4Zngk4o5rqGp .image-shape .label,#mermaid-svg-dLrx4Zngk4o5rqGp .icon-shape .label{text-anchor:middle;}#mermaid-svg-dLrx4Zngk4o5rqGp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dLrx4Zngk4o5rqGp .rough-node .label,#mermaid-svg-dLrx4Zngk4o5rqGp .node .label,#mermaid-svg-dLrx4Zngk4o5rqGp .image-shape .label,#mermaid-svg-dLrx4Zngk4o5rqGp .icon-shape .label{text-align:center;}#mermaid-svg-dLrx4Zngk4o5rqGp .node.clickable{cursor:pointer;}#mermaid-svg-dLrx4Zngk4o5rqGp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dLrx4Zngk4o5rqGp .arrowheadPath{fill:#333333;}#mermaid-svg-dLrx4Zngk4o5rqGp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dLrx4Zngk4o5rqGp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dLrx4Zngk4o5rqGp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dLrx4Zngk4o5rqGp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dLrx4Zngk4o5rqGp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dLrx4Zngk4o5rqGp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster text{fill:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp .cluster span{color:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp 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-dLrx4Zngk4o5rqGp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dLrx4Zngk4o5rqGp rect.text{fill:none;stroke-width:0;}#mermaid-svg-dLrx4Zngk4o5rqGp .icon-shape,#mermaid-svg-dLrx4Zngk4o5rqGp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dLrx4Zngk4o5rqGp .icon-shape p,#mermaid-svg-dLrx4Zngk4o5rqGp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dLrx4Zngk4o5rqGp .icon-shape .label rect,#mermaid-svg-dLrx4Zngk4o5rqGp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dLrx4Zngk4o5rqGp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dLrx4Zngk4o5rqGp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dLrx4Zngk4o5rqGp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java 堆
新生代
老年代
Eden 区
Survivor From 区
Survivor To 区

堆有几个关键特征:

特征 说明
线程共享 多个线程都可以访问堆中的对象
存放对象实例 普通对象、数组一般都在堆上
GC 重点区域 垃圾回收主要发生在堆中
可能 OOM 堆空间不足会抛出 OutOfMemoryError

典型堆溢出错误是:

text 复制代码
java.lang.OutOfMemoryError: Java heap space

常见触发原因包括:对象创建过多、集合无限增长、缓存没有淘汰、大对象频繁分配、内存泄漏导致对象无法回收。

对象一定都在堆上吗

严格说,不能把"对象都在堆上"说成绝对结论。

对普通业务理解来说,大多数对象实例和数组分配在堆里,这个说法没问题。但 HotSpot 在运行时会做优化,如果 JIT 编译器通过逃逸分析发现一个对象不会逃出当前方法或当前线程,就可能进一步做标量替换。

标量替换可以理解为:对象没有必要真的以完整对象形式分配出来,JVM 可以把它拆成几个普通字段来处理。

java 复制代码
public int sum() {
    Point p = new Point(1, 2);
    return p.x + p.y;
}

如果 p 没有逃离 sum 方法,JIT 有机会把它优化成类似下面的形式:

java 复制代码
public int sum() {
    int x = 1;
    int y = 2;
    return x + y;
}

这时从最终执行效果看,那个 Point 对象可能没有作为一个完整对象进入堆。
#mermaid-svg-k29RVDxaxs4meaJ7{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-k29RVDxaxs4meaJ7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k29RVDxaxs4meaJ7 .error-icon{fill:#552222;}#mermaid-svg-k29RVDxaxs4meaJ7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k29RVDxaxs4meaJ7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k29RVDxaxs4meaJ7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k29RVDxaxs4meaJ7 .marker.cross{stroke:#333333;}#mermaid-svg-k29RVDxaxs4meaJ7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k29RVDxaxs4meaJ7 p{margin:0;}#mermaid-svg-k29RVDxaxs4meaJ7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster-label text{fill:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster-label span{color:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster-label span p{background-color:transparent;}#mermaid-svg-k29RVDxaxs4meaJ7 .label text,#mermaid-svg-k29RVDxaxs4meaJ7 span{fill:#333;color:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 .node rect,#mermaid-svg-k29RVDxaxs4meaJ7 .node circle,#mermaid-svg-k29RVDxaxs4meaJ7 .node ellipse,#mermaid-svg-k29RVDxaxs4meaJ7 .node polygon,#mermaid-svg-k29RVDxaxs4meaJ7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k29RVDxaxs4meaJ7 .rough-node .label text,#mermaid-svg-k29RVDxaxs4meaJ7 .node .label text,#mermaid-svg-k29RVDxaxs4meaJ7 .image-shape .label,#mermaid-svg-k29RVDxaxs4meaJ7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-k29RVDxaxs4meaJ7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-k29RVDxaxs4meaJ7 .rough-node .label,#mermaid-svg-k29RVDxaxs4meaJ7 .node .label,#mermaid-svg-k29RVDxaxs4meaJ7 .image-shape .label,#mermaid-svg-k29RVDxaxs4meaJ7 .icon-shape .label{text-align:center;}#mermaid-svg-k29RVDxaxs4meaJ7 .node.clickable{cursor:pointer;}#mermaid-svg-k29RVDxaxs4meaJ7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-k29RVDxaxs4meaJ7 .arrowheadPath{fill:#333333;}#mermaid-svg-k29RVDxaxs4meaJ7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k29RVDxaxs4meaJ7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k29RVDxaxs4meaJ7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k29RVDxaxs4meaJ7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-k29RVDxaxs4meaJ7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k29RVDxaxs4meaJ7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster text{fill:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 .cluster span{color:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 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-k29RVDxaxs4meaJ7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-k29RVDxaxs4meaJ7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-k29RVDxaxs4meaJ7 .icon-shape,#mermaid-svg-k29RVDxaxs4meaJ7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k29RVDxaxs4meaJ7 .icon-shape p,#mermaid-svg-k29RVDxaxs4meaJ7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-k29RVDxaxs4meaJ7 .icon-shape .label rect,#mermaid-svg-k29RVDxaxs4meaJ7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k29RVDxaxs4meaJ7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-k29RVDxaxs4meaJ7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-k29RVDxaxs4meaJ7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 会逃逸
不逃逸
创建对象
是否逃离方法或线程
通常需要在堆上分配
JIT 可能优化
标量替换
锁消除

还有一个容易混的概念是 TLAB。TLAB 是 Thread Local Allocation Buffer,线程本地分配缓冲区。它不是栈,也不是新的内存区域,而是 Eden 区里给某个线程预留的一小块空间。这样线程创建对象时,很多时候可以先在自己的 TLAB 里分配,减少多线程竞争。
#mermaid-svg-FXV55wAAlRpuyd7b{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-FXV55wAAlRpuyd7b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FXV55wAAlRpuyd7b .error-icon{fill:#552222;}#mermaid-svg-FXV55wAAlRpuyd7b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FXV55wAAlRpuyd7b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FXV55wAAlRpuyd7b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FXV55wAAlRpuyd7b .marker.cross{stroke:#333333;}#mermaid-svg-FXV55wAAlRpuyd7b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FXV55wAAlRpuyd7b p{margin:0;}#mermaid-svg-FXV55wAAlRpuyd7b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FXV55wAAlRpuyd7b .cluster-label text{fill:#333;}#mermaid-svg-FXV55wAAlRpuyd7b .cluster-label span{color:#333;}#mermaid-svg-FXV55wAAlRpuyd7b .cluster-label span p{background-color:transparent;}#mermaid-svg-FXV55wAAlRpuyd7b .label text,#mermaid-svg-FXV55wAAlRpuyd7b span{fill:#333;color:#333;}#mermaid-svg-FXV55wAAlRpuyd7b .node rect,#mermaid-svg-FXV55wAAlRpuyd7b .node circle,#mermaid-svg-FXV55wAAlRpuyd7b .node ellipse,#mermaid-svg-FXV55wAAlRpuyd7b .node polygon,#mermaid-svg-FXV55wAAlRpuyd7b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FXV55wAAlRpuyd7b .rough-node .label text,#mermaid-svg-FXV55wAAlRpuyd7b .node .label text,#mermaid-svg-FXV55wAAlRpuyd7b .image-shape .label,#mermaid-svg-FXV55wAAlRpuyd7b .icon-shape .label{text-anchor:middle;}#mermaid-svg-FXV55wAAlRpuyd7b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FXV55wAAlRpuyd7b .rough-node .label,#mermaid-svg-FXV55wAAlRpuyd7b .node .label,#mermaid-svg-FXV55wAAlRpuyd7b .image-shape .label,#mermaid-svg-FXV55wAAlRpuyd7b .icon-shape .label{text-align:center;}#mermaid-svg-FXV55wAAlRpuyd7b .node.clickable{cursor:pointer;}#mermaid-svg-FXV55wAAlRpuyd7b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FXV55wAAlRpuyd7b .arrowheadPath{fill:#333333;}#mermaid-svg-FXV55wAAlRpuyd7b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FXV55wAAlRpuyd7b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FXV55wAAlRpuyd7b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FXV55wAAlRpuyd7b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FXV55wAAlRpuyd7b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FXV55wAAlRpuyd7b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FXV55wAAlRpuyd7b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FXV55wAAlRpuyd7b .cluster text{fill:#333;}#mermaid-svg-FXV55wAAlRpuyd7b .cluster span{color:#333;}#mermaid-svg-FXV55wAAlRpuyd7b 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-FXV55wAAlRpuyd7b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FXV55wAAlRpuyd7b rect.text{fill:none;stroke-width:0;}#mermaid-svg-FXV55wAAlRpuyd7b .icon-shape,#mermaid-svg-FXV55wAAlRpuyd7b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FXV55wAAlRpuyd7b .icon-shape p,#mermaid-svg-FXV55wAAlRpuyd7b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FXV55wAAlRpuyd7b .icon-shape .label rect,#mermaid-svg-FXV55wAAlRpuyd7b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FXV55wAAlRpuyd7b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FXV55wAAlRpuyd7b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FXV55wAAlRpuyd7b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Eden 区
线程 1 的 TLAB
线程 2 的 TLAB
公共 Eden 空间

所以更严谨的说法是:从 JVM 内存模型和普通业务视角看,对象主要分配在堆上;从 JIT 优化视角看,部分没有逃逸的对象可能被优化掉,不一定真的形成完整堆对象。

虚拟机栈

虚拟机栈也是线程私有的。每个线程启动时都会有自己的虚拟机栈,方法每调用一次,就会创建一个栈帧并压入栈中;方法执行结束,栈帧弹出。
#mermaid-svg-aSKy3DMDjrzhh784{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-aSKy3DMDjrzhh784 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aSKy3DMDjrzhh784 .error-icon{fill:#552222;}#mermaid-svg-aSKy3DMDjrzhh784 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aSKy3DMDjrzhh784 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aSKy3DMDjrzhh784 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aSKy3DMDjrzhh784 .marker.cross{stroke:#333333;}#mermaid-svg-aSKy3DMDjrzhh784 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aSKy3DMDjrzhh784 p{margin:0;}#mermaid-svg-aSKy3DMDjrzhh784 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aSKy3DMDjrzhh784 .cluster-label text{fill:#333;}#mermaid-svg-aSKy3DMDjrzhh784 .cluster-label span{color:#333;}#mermaid-svg-aSKy3DMDjrzhh784 .cluster-label span p{background-color:transparent;}#mermaid-svg-aSKy3DMDjrzhh784 .label text,#mermaid-svg-aSKy3DMDjrzhh784 span{fill:#333;color:#333;}#mermaid-svg-aSKy3DMDjrzhh784 .node rect,#mermaid-svg-aSKy3DMDjrzhh784 .node circle,#mermaid-svg-aSKy3DMDjrzhh784 .node ellipse,#mermaid-svg-aSKy3DMDjrzhh784 .node polygon,#mermaid-svg-aSKy3DMDjrzhh784 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aSKy3DMDjrzhh784 .rough-node .label text,#mermaid-svg-aSKy3DMDjrzhh784 .node .label text,#mermaid-svg-aSKy3DMDjrzhh784 .image-shape .label,#mermaid-svg-aSKy3DMDjrzhh784 .icon-shape .label{text-anchor:middle;}#mermaid-svg-aSKy3DMDjrzhh784 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aSKy3DMDjrzhh784 .rough-node .label,#mermaid-svg-aSKy3DMDjrzhh784 .node .label,#mermaid-svg-aSKy3DMDjrzhh784 .image-shape .label,#mermaid-svg-aSKy3DMDjrzhh784 .icon-shape .label{text-align:center;}#mermaid-svg-aSKy3DMDjrzhh784 .node.clickable{cursor:pointer;}#mermaid-svg-aSKy3DMDjrzhh784 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aSKy3DMDjrzhh784 .arrowheadPath{fill:#333333;}#mermaid-svg-aSKy3DMDjrzhh784 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aSKy3DMDjrzhh784 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aSKy3DMDjrzhh784 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aSKy3DMDjrzhh784 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aSKy3DMDjrzhh784 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aSKy3DMDjrzhh784 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aSKy3DMDjrzhh784 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aSKy3DMDjrzhh784 .cluster text{fill:#333;}#mermaid-svg-aSKy3DMDjrzhh784 .cluster span{color:#333;}#mermaid-svg-aSKy3DMDjrzhh784 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-aSKy3DMDjrzhh784 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aSKy3DMDjrzhh784 rect.text{fill:none;stroke-width:0;}#mermaid-svg-aSKy3DMDjrzhh784 .icon-shape,#mermaid-svg-aSKy3DMDjrzhh784 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aSKy3DMDjrzhh784 .icon-shape p,#mermaid-svg-aSKy3DMDjrzhh784 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aSKy3DMDjrzhh784 .icon-shape .label rect,#mermaid-svg-aSKy3DMDjrzhh784 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aSKy3DMDjrzhh784 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aSKy3DMDjrzhh784 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aSKy3DMDjrzhh784 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程开始执行
调用 main 方法
压入 main 栈帧
调用 service 方法
压入 service 栈帧
调用 dao 方法
压入 dao 栈帧
dao 执行结束
弹出 dao 栈帧
service 执行结束
弹出 service 栈帧

栈帧里通常包含局部变量表、操作数栈、动态链接、方法返回地址等信息。面试中不一定要展开到每个结构,但至少要能说清楚:栈帧对应一次方法调用。
#mermaid-svg-qxOduClKsIpMLc61{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-qxOduClKsIpMLc61 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qxOduClKsIpMLc61 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qxOduClKsIpMLc61 .error-icon{fill:#552222;}#mermaid-svg-qxOduClKsIpMLc61 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qxOduClKsIpMLc61 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qxOduClKsIpMLc61 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qxOduClKsIpMLc61 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qxOduClKsIpMLc61 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qxOduClKsIpMLc61 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qxOduClKsIpMLc61 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qxOduClKsIpMLc61 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qxOduClKsIpMLc61 .marker.cross{stroke:#333333;}#mermaid-svg-qxOduClKsIpMLc61 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qxOduClKsIpMLc61 p{margin:0;}#mermaid-svg-qxOduClKsIpMLc61 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qxOduClKsIpMLc61 .cluster-label text{fill:#333;}#mermaid-svg-qxOduClKsIpMLc61 .cluster-label span{color:#333;}#mermaid-svg-qxOduClKsIpMLc61 .cluster-label span p{background-color:transparent;}#mermaid-svg-qxOduClKsIpMLc61 .label text,#mermaid-svg-qxOduClKsIpMLc61 span{fill:#333;color:#333;}#mermaid-svg-qxOduClKsIpMLc61 .node rect,#mermaid-svg-qxOduClKsIpMLc61 .node circle,#mermaid-svg-qxOduClKsIpMLc61 .node ellipse,#mermaid-svg-qxOduClKsIpMLc61 .node polygon,#mermaid-svg-qxOduClKsIpMLc61 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qxOduClKsIpMLc61 .rough-node .label text,#mermaid-svg-qxOduClKsIpMLc61 .node .label text,#mermaid-svg-qxOduClKsIpMLc61 .image-shape .label,#mermaid-svg-qxOduClKsIpMLc61 .icon-shape .label{text-anchor:middle;}#mermaid-svg-qxOduClKsIpMLc61 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qxOduClKsIpMLc61 .rough-node .label,#mermaid-svg-qxOduClKsIpMLc61 .node .label,#mermaid-svg-qxOduClKsIpMLc61 .image-shape .label,#mermaid-svg-qxOduClKsIpMLc61 .icon-shape .label{text-align:center;}#mermaid-svg-qxOduClKsIpMLc61 .node.clickable{cursor:pointer;}#mermaid-svg-qxOduClKsIpMLc61 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qxOduClKsIpMLc61 .arrowheadPath{fill:#333333;}#mermaid-svg-qxOduClKsIpMLc61 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qxOduClKsIpMLc61 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qxOduClKsIpMLc61 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qxOduClKsIpMLc61 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qxOduClKsIpMLc61 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qxOduClKsIpMLc61 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qxOduClKsIpMLc61 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qxOduClKsIpMLc61 .cluster text{fill:#333;}#mermaid-svg-qxOduClKsIpMLc61 .cluster span{color:#333;}#mermaid-svg-qxOduClKsIpMLc61 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-qxOduClKsIpMLc61 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qxOduClKsIpMLc61 rect.text{fill:none;stroke-width:0;}#mermaid-svg-qxOduClKsIpMLc61 .icon-shape,#mermaid-svg-qxOduClKsIpMLc61 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qxOduClKsIpMLc61 .icon-shape p,#mermaid-svg-qxOduClKsIpMLc61 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qxOduClKsIpMLc61 .icon-shape .label rect,#mermaid-svg-qxOduClKsIpMLc61 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qxOduClKsIpMLc61 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qxOduClKsIpMLc61 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qxOduClKsIpMLc61 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一个栈帧
局部变量表
操作数栈
动态链接
方法返回地址

垃圾回收是否涉及栈内存

通常说垃圾回收,主要指堆内存和方法区里的回收。虚拟机栈不需要像堆一样做复杂 GC,因为方法执行结束后,栈帧会自然弹出,对应内存也随之释放。
#mermaid-svg-0Qr4Sx4HPbgug9Nr{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-0Qr4Sx4HPbgug9Nr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .error-icon{fill:#552222;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .marker.cross{stroke:#333333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr p{margin:0;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster-label text{fill:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster-label span{color:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster-label span p{background-color:transparent;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .label text,#mermaid-svg-0Qr4Sx4HPbgug9Nr span{fill:#333;color:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .node rect,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node circle,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node ellipse,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node polygon,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .rough-node .label text,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node .label text,#mermaid-svg-0Qr4Sx4HPbgug9Nr .image-shape .label,#mermaid-svg-0Qr4Sx4HPbgug9Nr .icon-shape .label{text-anchor:middle;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .rough-node .label,#mermaid-svg-0Qr4Sx4HPbgug9Nr .node .label,#mermaid-svg-0Qr4Sx4HPbgug9Nr .image-shape .label,#mermaid-svg-0Qr4Sx4HPbgug9Nr .icon-shape .label{text-align:center;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .node.clickable{cursor:pointer;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .arrowheadPath{fill:#333333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0Qr4Sx4HPbgug9Nr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0Qr4Sx4HPbgug9Nr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster text{fill:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .cluster span{color:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr 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-0Qr4Sx4HPbgug9Nr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0Qr4Sx4HPbgug9Nr rect.text{fill:none;stroke-width:0;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .icon-shape,#mermaid-svg-0Qr4Sx4HPbgug9Nr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .icon-shape p,#mermaid-svg-0Qr4Sx4HPbgug9Nr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .icon-shape .label rect,#mermaid-svg-0Qr4Sx4HPbgug9Nr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0Qr4Sx4HPbgug9Nr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0Qr4Sx4HPbgug9Nr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0Qr4Sx4HPbgug9Nr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 方法调用
栈帧入栈
方法执行
方法返回
栈帧出栈
栈帧内存释放

但这不代表栈和 GC 完全没有关系。栈帧中的局部变量表可以保存对象引用,这些引用可能成为 GC Roots,影响堆中对象是否可回收。

栈内存越大越好吗

不一定。

每个线程都有自己的栈空间,单个线程栈越大,同样的机器内存下能创建的线程数就越少。

比如总内存固定时:

单线程栈大小 影响
较小 可以创建更多线程,但递归深度和大栈帧空间更有限
较大 单线程可用栈更深,但线程总数可能下降

可以通过 -Xss 调整每个线程的栈大小,例如:

bash 复制代码
java -Xss256k -jar app.jar

实际项目里不要随意把 -Xss 调得很大。线程数、递归深度、框架调用层级和操作系统限制都要一起考虑。

方法内局部变量是否线程安全

这个问题要分情况。

如果局部变量没有逃离方法作用域,它通常是线程安全的。因为每个线程都有自己的栈帧,局部变量表互不共享。

java 复制代码
public void m1() {
    StringBuilder sb = new StringBuilder();
    sb.append("a");
    sb.append("b");
}

这里的 sb 只在当前方法内部使用,不会被其他线程拿到。

但如果局部变量引用的对象逃离了方法作用域,就要考虑线程安全。

java 复制代码
public void m2(StringBuilder sb) {
    sb.append("a");
    sb.append("b");
}

sb 这个引用变量本身在当前线程栈帧里,但它指向的对象可能被多个线程共享。线程安全问题不取决于"变量是不是局部变量",而取决于"变量引用的对象有没有被共享、有没有被并发修改"。
#mermaid-svg-o4bafC9ZVInkxqEQ{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-o4bafC9ZVInkxqEQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o4bafC9ZVInkxqEQ .error-icon{fill:#552222;}#mermaid-svg-o4bafC9ZVInkxqEQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o4bafC9ZVInkxqEQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o4bafC9ZVInkxqEQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o4bafC9ZVInkxqEQ .marker.cross{stroke:#333333;}#mermaid-svg-o4bafC9ZVInkxqEQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o4bafC9ZVInkxqEQ p{margin:0;}#mermaid-svg-o4bafC9ZVInkxqEQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster-label text{fill:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster-label span{color:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster-label span p{background-color:transparent;}#mermaid-svg-o4bafC9ZVInkxqEQ .label text,#mermaid-svg-o4bafC9ZVInkxqEQ span{fill:#333;color:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ .node rect,#mermaid-svg-o4bafC9ZVInkxqEQ .node circle,#mermaid-svg-o4bafC9ZVInkxqEQ .node ellipse,#mermaid-svg-o4bafC9ZVInkxqEQ .node polygon,#mermaid-svg-o4bafC9ZVInkxqEQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o4bafC9ZVInkxqEQ .rough-node .label text,#mermaid-svg-o4bafC9ZVInkxqEQ .node .label text,#mermaid-svg-o4bafC9ZVInkxqEQ .image-shape .label,#mermaid-svg-o4bafC9ZVInkxqEQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-o4bafC9ZVInkxqEQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-o4bafC9ZVInkxqEQ .rough-node .label,#mermaid-svg-o4bafC9ZVInkxqEQ .node .label,#mermaid-svg-o4bafC9ZVInkxqEQ .image-shape .label,#mermaid-svg-o4bafC9ZVInkxqEQ .icon-shape .label{text-align:center;}#mermaid-svg-o4bafC9ZVInkxqEQ .node.clickable{cursor:pointer;}#mermaid-svg-o4bafC9ZVInkxqEQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-o4bafC9ZVInkxqEQ .arrowheadPath{fill:#333333;}#mermaid-svg-o4bafC9ZVInkxqEQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o4bafC9ZVInkxqEQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o4bafC9ZVInkxqEQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o4bafC9ZVInkxqEQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-o4bafC9ZVInkxqEQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o4bafC9ZVInkxqEQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster text{fill:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ .cluster span{color:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ 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-o4bafC9ZVInkxqEQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-o4bafC9ZVInkxqEQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-o4bafC9ZVInkxqEQ .icon-shape,#mermaid-svg-o4bafC9ZVInkxqEQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o4bafC9ZVInkxqEQ .icon-shape p,#mermaid-svg-o4bafC9ZVInkxqEQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-o4bafC9ZVInkxqEQ .icon-shape .label rect,#mermaid-svg-o4bafC9ZVInkxqEQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o4bafC9ZVInkxqEQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-o4bafC9ZVInkxqEQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-o4bafC9ZVInkxqEQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否



局部变量引用对象
对象是否逃离当前方法
通常线程安全
是否被多个线程修改
通常问题不大
需要考虑线程安全

栈内存溢出

栈内存溢出最常见的原因是递归调用过深。

java 复制代码
public static void loop() {
    loop();
}

执行后可能抛出:

text 复制代码
java.lang.StackOverflowError

栈溢出常见原因有两类:

原因 说明
栈帧过多 无限递归、递归终止条件错误
栈帧过大 方法局部变量过多或调用链过深

排查时重点看异常栈里反复出现的方法名。如果同一个方法不断重复,基本就是递归或循环调用链出了问题。

堆和栈的区别

堆和栈是最容易混的两个区域,可以这样对比:

对比项 虚拟机栈
是否线程共享 共享 私有
存储内容 对象实例、数组 方法调用栈帧、局部变量等
生命周期 对象由 GC 判断是否回收 方法调用结束栈帧自然释放
常见异常 OutOfMemoryError StackOverflowError
是否 GC 重点区域 不是主要回收区域

一句话记忆:对象主要在堆里,方法调用过程主要在栈里。

面试回答模板

可以这样回答:

JVM 运行时数据区分为线程私有和线程共享两类。线程私有的有程序计数器、虚拟机栈、本地方法栈;线程共享的有堆和方法区。程序计数器保存当前线程执行到哪条字节码指令。虚拟机栈保存方法调用过程,每次方法调用都会创建一个栈帧,方法结束栈帧弹出。堆是线程共享的,主要存放对象实例和数组,也是垃圾回收最主要的区域。栈一般不需要 GC,栈帧出栈内存就释放,但栈帧里的对象引用可能作为 GC Roots 影响堆对象回收。堆空间不足会出现 OutOfMemoryError,栈递归过深通常会出现 StackOverflowError

小结

JVM 内存结构最重要的不是背名词,而是把关系讲清楚:

线程执行位置靠程序计数器保存,方法调用靠虚拟机栈维护,对象实例主要分配在堆里,堆里的对象能不能回收要看引用关系。

这条线清楚了,后面的 GC、内存泄漏和线上排障都会更容易理解。

相关推荐
凡人叶枫2 小时前
Effective C++ 条款10:令 operator= 返回一个 reference to *this
java·linux·服务器·开发语言·c++·effective c++
摇滚侠2 小时前
JavaSE 和 JavaEE 是什么意思
java·java-ee
想带你从多云到转晴2 小时前
03、JAVAEE---多线程(三)
java
满怀冰雪2 小时前
第04篇-双指针算法-从有序数组到回文判断的高频解法
java·算法
matlabgoodboy2 小时前
计算机java程序代写python代码编写c/c++代做qt设计php开发matlab
java·c语言·python
视觉小萌新2 小时前
C++利用libmicrohttpd制作交互网页端——C1
java·c++·交互
Gauss松鼠会2 小时前
【GaussDB】GaussDB SMP特性调优详解
java·服务器·前端·数据库·sql·算法·gaussdb
格发许可优化管理系统3 小时前
Mentor许可证使用规定全解析
java·大数据·c语言·开发语言·c++
JAVA面经实录9173 小时前
Redis 知识体系(完整版)
java·redis·nosql数据库·nosql