Sunday面试指南

JVM 内存区域有哪些?堆、虚拟机栈和元空间分别存什么?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:Java 的对象存在哪里?

🙋‍♂️ 我:对象在堆里,局部变量在栈里。

🧑‍💻 面试官:方法里的局部变量引用了一个对象。这个对象也跟着进入栈了吗?

🙋‍♂️ 我:没有,栈里的变量可以保存指向对象的引用。

🧑‍💻 面试官:那应用报 Metaspace 内存不足,把最大堆调大能解决吗?你说的方法区和元空间又是什么关系?

先抓住「装的是什么、谁在使用」:方法调用过程、对象实例和类元数据,不能都当成同一块内存。

面试速答(60 秒版)

JVM 规范把运行时内存分成程序计数器、虚拟机栈、堆、方法区和本地方法栈等逻辑区域,运行时常量池属于方法区的一部分。

程序计数器和虚拟机栈是线程私有的。每次方法调用都会对应一个栈帧,保存局部变量、操作数栈等执行信息。堆则供线程共享,在逻辑上分配对象实例和数组。

方法区保存类相关的结构。在现代 HotSpot 中,类元数据主要使用本地内存中的 Metaspace,但元空间不能简单替代规范里方法区的全部含义。例如 JIT 编译后的机器代码还有自己的 Code Cache。

排查内存问题时,先看错误发生在哪个区域。栈深度、堆中的对象保留和类元数据增长,需要用不同证据分析,不能一看到内存不足就调大 Xmx。

JVM 内存:先分线程与共享

知识点详解:沿着一次方法调用认识这些区域

方法在执行,栈里就会留下它的执行现场

假设一个请求进入 service 方法,service 再调用 parse 方法。

线程调用 service 时建立一个栈帧,调用 parse 时又建立一个。parse 结束后,其栈帧退出,线程回到 service 继续执行。

栈帧里的局部变量可以保存数值,也可以保存对象引用。一个引用在栈里,不意味着它指向的整个对象也在栈里。不同线程还可能通过不同引用访问同一个共享对象。

程序计数器则帮助描述当前线程执行到哪条 JVM 指令。切换线程以后,各线程有自己的执行位置,不会共用一个进度标记。

对象在堆里,方法结束也未必消失

继续假设 service 调用 parse,而 parse 在处理时创建一个结果对象。parse 返回后,它自己的栈帧退出,service 用 result 接住返回值,仍然引用着这个对象。

如果 service 随后再把结果返回给外层调用者,道理也一样:service 的栈帧虽然退出了,只要调用者还持有这个结果,对象就不会仅仅因为方法结束而被回收。

因此,栈帧的生命周期跟着方法调用走,对象的存活则要看引用关系。这两个问题不要混在一起。

JVMS 25规定堆用于对象实例和数组的逻辑分配。实际实现可以通过逃逸分析等优化消除某些分配,所以也不要把“每个 new 都一定留下一个实际堆对象”说成不可优化的物理事实。

栈帧退出,对象未必消失

方法区是规范名称,元空间是实现里的概念

JVM 需要知道一个类有哪些字段、有哪些方法,以及相关的运行时常量等结构。这些属于类层面的信息,不是某一个用户对象的实例数据。

方法区描述的是这类信息所在的逻辑区域。HotSpot 的 Metaspace 主要管理类元数据,使用本地内存。JDK 8 以后的相关实现,不应该再用永久代作为当前默认解释。

同时,实例字段数据、类元数据、Class 对象和 JIT 机器代码,也不是一看到“类”字就都放进同一处。面试中先说清规范与实现的区别,比画一张过度简化的“元空间装所有方法相关内容”更稳妥。

三种内存异常,先查三个不同方向

如果报 StackOverflowError,先检查调用链是不是太深,例如没有退出条件的递归。增加栈大小可能只是把失败推迟,不解决递归逻辑。

如果报 Java heap space,要看堆中的对象为什么增长:请求积压、大结果集、缓存没有边界,还是对象本该释放却仍被引用。可以结合堆转储、对象数量和引用路径分析。

如果报 Metaspace,要检查类加载数量和类加载器生命周期。动态生成代理类、反复加载插件但旧加载器没有释放,都可能让类元数据持续保留。只调大堆容量不对应这个区域。

当然,进程总内存还包括其他本地内存,不能拿 Java 堆大小代表整个进程的内存消耗。

三种异常,查三个方向

面试官继续追问

栈是私有的,就说明线程一定不会相互影响吗?

不说明。线程私有的是调用栈,栈里的引用仍可能指向共享对象。两个线程访问同一个可变对象,仍需要正确的并发控制。

元空间使用本地内存,是不是就没有上限了?

不是。它仍受进程与操作系统可用内存约束,也可以配置相应上限。本地内存不是免费内存,更不是无限内存。

怎么区分对象增长和类增长?

查看堆中主要对象的数量与保留路径,同时观察已加载类、类加载器和元空间使用情况。收集证据要对应症状;堆转储并不能单独代表所有本地内存情况。

面试速记卡

  • 栈:线程的方法调用现场,按栈帧管理执行信息。
  • 堆:逻辑上的对象实例与数组分配区域,线程共享。
  • 方法区:规范中的类相关逻辑区域。
  • Metaspace:HotSpot 类元数据实现,不能概括所有方法区内容。
  • 排错:先定位区域,再看调用深度、对象保留或类加载器。

公司面试真题

真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。

  • 美团 · Java后端 · 实习

    JVM 内存结构分为哪些部分,各有什么作用?(题意整理)

    4.21美团Java实习一二面面经 ↗
    面试记录为 2020-04-21、2020-04-24;原帖编辑于 2020-11-14

  • 京东 · Java后台 · 校招

    JVM 内存怎样分区?(题意整理)

    京东 Java 后台三面凉经 ↗
    原帖编辑于 2019-08-23(历史校招面经)

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历