native方法调用时栈帧仍压在java栈上,不切换至独立本地方法栈;本地代码运行于os原生栈,与jvm栈物理分离但hotspot中二者内存共享;jni回调java会新增栈帧,形成嵌套调用链。

Native方法调用时,栈帧其实压在Java栈上
很多人以为调用native方法会立刻“跳转到本地方法栈”,但HotSpot JVM实际做法是:在当前线程的Java虚拟机栈中照常压入一个栈帧——它包含参数、返回地址、局部变量表等,和普通Java方法完全一致。这个栈帧不会被丢弃或迁移,而是持续存在并保持GC可达性(比如其中引用的对象仍是GC Root)。控制权交给JNI后,该栈帧进入“挂起等待返回”状态,不参与后续字节码执行,但内存和语义都未失效。
本地代码运行在操作系统原生栈,与JVM栈无关
C/C++实现的native函数并不运行在JVM管理的任何栈内存里,而是在OS线程的原生栈上执行(如Linux下的pthread栈、Windows下的线程栈)。这意味着:
- native函数内部的递归、局部变量分配、甚至创建新线程,都不受-Xss限制
- JVM不保存或恢复其寄存器上下文,也不干涉其栈空间使用
- 若native代码调用了阻塞系统调用(如
read()、pthread_cond_wait()),触发的是OS级线程挂起,此时jstack会显示线程状态为NATIVE
两栈在HotSpot中物理合并,逻辑仍分离
从JVM规范看,虚拟机栈服务Java方法,本地方法栈服务native方法,二者职责不同;但在HotSpot实现中,它们共享同一块内存区域,统一由-Xss参数控制大小。也就是说:
- 不存在独立的“本地方法栈内存池”,也不会单独报
StackOverflowError(错误堆栈仍归属Java栈) - 虽然逻辑上区分“Java栈帧”和“native栈帧”,但HotSpot不为native方法新建栈结构,也不维护独立的栈指针
- 真正区分调用来源靠的是字节码指令:
invokestatic遇到native方法时触发JNI绑定,而非栈切换
回调Java方法会嵌套生成新Java栈帧
当native代码通过JNIEnv->CallObjectMethod()等JNI接口反向调用Java方法时,JVM会在同一线程的Java虚拟机栈上新增栈帧,形成Java → native → Java的嵌套调用链。此时:
- 新栈帧与原始Java栈帧共存于同一栈结构中
- GC仍能追踪全部栈帧中的对象引用
- 异常抛出、同步锁(如native synchronized)、finally块等Java语义全部生效











