很多面试题第一题就是:“说一下JVM的内存模型”。你要是直接背《深入理解Java虚拟机》里的原文,面试官可能自己都听困了。咱们换个姿势——把JVM想象成一家公司,内存就是公司的办公区,马上你就懂了。
一、先看全局:公司里有哪几类区域?
JVM内存主要分五块:堆、虚拟机栈、本地方法栈、方法区、程序计数器。我们主要关注前三块+方法区,程序计数器太小了,就像每个人手里的“当前做到哪一行”的小便签,暂时忽略。
用公司比喻:
- 堆(Heap):公司的大仓库,所有员工(线程)都能往里放东西、取东西。
- 虚拟机栈(VM Stack):每个员工自己的工位+抽屉,别人进不来。
- 方法区(Method Area):公司的规章制度+员工手册,所有人都能查阅,但基本不改。
- 本地方法栈:专门用来和外部合作方(比如C语言写的底层系统)打交道的窗口。
二、堆(Heap):公共大仓库
堆是内存里最大的一块,所有线程共享。你new出来的对象、数组,都放在这里。
比喻: 堆就像公司的大仓库。产品部放了一箱样品,销售部也能拿去给客户看;技术部放了一台测试机,运营部也能借用。谁都能存取。
特点:
- 垃圾回收(GC)主要在这儿干活。没人用的东西,仓库管理员会定期清理。
- 仓库有大小限制,塞太满就报
OutOfMemoryError(俗称OOM)。比如你不断往ArrayList里add对象,又不释放,仓库就炸了。
面试关键词: 线程共享、GC主要区域、堆溢出(OOM)。
三、栈(VM Stack):每个人的工位
每个线程(可以理解成一个员工)都有自己的栈。栈里放的是栈帧——每个方法调用对应一个栈帧,里面存着局部变量、操作数、方法出口等信息。
比喻: 栈就是你的工位。你干活的时候,桌上会摆一堆资料(局部变量)。你的活儿干完了,就把这堆资料收走,工位腾出来给下一个任务。别人不能坐你的工位,也动不了你桌上的东西。
特点:
- 栈是线程私有的,天然线程安全(因为别人碰不到)。
- 递归调用太深,比如
main -> a -> b -> a -> b……一直不返回,栈帧叠罗汉,最终工位堆不下,报StackOverflowError。 - 栈内存一般不大,但存取极快,用完自动释放,不需要GC。
面试关键词: 线程私有、方法调用栈、栈溢出。
四、方法区(Method Area):公司的规章制度
方法区也是线程共享的。它存储类信息(类名、方法、字段)、静态变量、常量池等。在HotSpot虚拟机中,老版本叫“永久代”,新版本叫“元空间”。
比喻: 方法区就是公司的员工手册、报销制度、组织架构图。所有人入职都要看,平时办事也要查。手册里的内容基本不变(类加载后就不改了),偶尔HR会更新一下(比如热部署)。
特点:
- 线程共享。
- 如果加载的类太多(比如用了大量动态代理、JSP),也会OOM。
- JDK 1.8以后,方法区从堆里移到了本地内存,上限更大,但也不是无限。
面试关键词: 类信息、静态变量、常量池、元空间。
五、一张图总结
| 区域 | 比喻 | 共享? | 报什么错? |
|---|---|---|---|
| 堆 | 大仓库 | 是 | OOM(堆溢出) |
| 栈 | 工位 | 否(线程私有) | StackOverflowError |
| 方法区 | 员工手册 | 是 | OOM(元空间溢出) |
六、面试官爱怎么问?
问: “一个对象从创建到变成垃圾,它在内存里经历了什么?”
答(白话版): 对象出生在堆里,它的类信息放在方法区。被栈里的局部变量引用着,所以活着。没人引用它以后,GC盯上它,在某次回收时把它清理掉。
问: “栈溢出和堆溢出有什么区别?”
答(白话版): 栈溢出是递归太深或方法调用层数太多,工位挤爆了;堆溢出是对象创建太多、内存泄漏,大仓库堆满了。栈溢出一般是程序bug,堆溢出要查内存泄漏。
最后送你一句人话总结:
JVM内存 = 公共仓库(堆)+ 私人工位(栈)+ 员工手册(方法区)。搞清楚谁共享、谁私有、哪里会爆,面试第一关就过了。
汇总导航