JVM—面试官:你简历写着懂JVM,那说说运行时数据区在内存里到底长什么样?
目录
3.6 本地方法栈(Native Method Stack)
一、JVM的内存抽象(宏观视角)

二、底层实现:HotSpot源码级解读
3.1 JVM是怎么启动的?
在HotSpot源码里,JVM的启动入口是 java.c 中的 JLI_Launch() 函数
关键调用链:
JLI_Launch() → InitializeJVM() → JNI_CreateJavaVM() → Threads::create_vm()
在 Threads::create_vm() 里,会调用 init_globals(),这个函数初始化了所有运行时数据区的核心组件
3.2 堆(Heap)的底层实现
对应C++类: CollectedHeap
在 init_globals() 中调用:
jint init_globals() {
// ...
universe_init(); // 初始化堆
// ...
}
universe_init() 最终会调用 Universe::initialize_heap(),根据你选择的GC(垃圾回收器)创建不同的堆实现:
-
ParallelGC:
ParallelScavengeHeap -
G1:
G1CollectedHeap -
ZGC:
ZCollectedHeap
堆的内存从哪里来?
-
通过
os::reserve_memory()向操作系统申请虚拟内存 -
通过
os::commit_memory()提交物理内存
本质: JVM堆就是一大块从操作系统申请来的连续虚拟地址空间,由JVM自己管理。
3.3 栈(VM Stack)的底层实现
对应C++类: JavaThread
每个Java线程在JVM内部对应一个 JavaThread 对象
call_run() 会调用 JavaCalls::call(),最终执行Java代码
栈帧(Stack Frame)的实现:
-
每个方法调用对应一个栈帧
-
栈帧在C++里没有直接对应的类,而是通过指针偏移来操作内存
-
包括:局部变量表、操作数栈、动态链接、方法返回地址
栈溢出是怎么发生的?
在 generate_fixed_frame() 生成栈帧时,会检查栈剩余空间:
if (stack_overflow_check && !guard_pages_allocated) {
// 检查是否触及栈保护页
// 如果触及,抛出 StackOverflowError
}
3.4 方法区(Method Area)的底层实现
在JDK7及以前: 方法区 = 永久代(PermGen),在堆外
在JDK8及以后: 方法区 = 元空间(Metaspace),使用本地内存
对应C++类: Metaspace
关键初始化:
void Metaspace::global_initialize() {
// 初始化元空间
MetaspaceGC::initialize();
_space_list = new MetaspaceArena(...);
}
元空间的内存从哪里来?
-
直接调用
os::malloc()从操作系统申请 -
不受JVM堆大小限制,只受本机内存限制
3.5 程序计数器(PC Register)的底层实现
对应C++: 在 JavaThread 中有两个关键字段:
class JavaThread {
address _pc; // 当前执行的字节码地址
int _bci; // 字节码索引(Byte Code Index)
}
3.6 本地方法栈(Native Method Stack)
对于HotSpot: Java栈和本地方法栈是同一个栈
在 JavaThread 创建时:
JavaThread::JavaThread() {
_stack_base = os::current_stack_base();
_stack_size = os::current_stack_size();
}
不管是Java方法还是本地方法,都在这个栈上分配栈帧,只是栈帧的格式不同
JVM的运行时数据区,本质上就是JVM向操作系统申请的一块块内存,通过C++的数据结构(类、指针、数组)进行管理,再通过JNI桥接给Java代码使用
更多推荐


所有评论(0)