女生做蛋糕甜品屋宝宝
105.50M · 2026-04-17
来自玩Android网站上的一个提问: wanandroid.com/wenda/show/…
当我们递归调用Java方法时,很可能会出现StackOverflowError,我们会认为此时栈内存溢出了,那么这个栈内存溢出虚拟机是如何检测的呢?
是累加分配的内存与栈大小进行比较,还是有更好的方式呢?
不是靠“累加分配大小做比较”,而是靠“访问受保护栈页面触发异常 + JVM 内部栈检查”来检测的。
分两层来看:操作系统层和 JVM 层(以 HotSpot 为例)。
线程栈本质是 OS 提供的一块连续虚拟内存区域,JVM 只是使用它。
典型做法(不同 OS 实现略有差异):
每个线程启动时 OS 会为其保留一块栈空间(如 1M、2M),其中一部分是真正可用的内存。
在栈的“尽头”会预留一小段“保护页(guard page)”:
在 HotSpot 中,每次方法调用、局部变量分配时,都会通过所谓的 stack banging 机制在即将申请的栈空间中访问特定地址,以确保一旦越界就立刻触发 OS 的异常,而不是“悄悄越界”。
这一步就保证了:栈真的用到边界时,OS 一定会抛异常出来。
JVM 收到来自 OS 的栈溢出信号时,它知道是当前线程栈用完了,然后:
**StackOverflowError**;这里有两种情况:
StackOverflowError;从设计和实现上,简单做“累加计数”有一些问题:
无法覆盖所有情况即使 JVM 在建帧时做了 剩余栈空间 >= 预计需要空间? 的检查,native 代码中还可能再消耗一些栈,此时 JVM 并不知道。
OS 本身已经提供了更可靠的栈溢出检测机制操作系统比 JVM 更清楚“栈的真实边界”,一旦页面访问越界,它能最准确、最及时地通知进程。
所以实际实现是:
以 HotSpot 为例,它一般会把线程栈分成几段逻辑区域:
启动 JVM 时通过 -Xss 指定每个线程的栈大小,影响的是这块区域的总规模。
但具体的检测点、抛异常的逻辑,不靠简单“加减计数”,而是靠 guard page + OS 异常 + JVM 处理。