提升后端知识 第一天测试
📝 JVM 运行时数据区 · 全面通关测试题(试卷 + 答案)
本测试题基于你第一天的学习内容设计,覆盖核心概念、细节辨析、场景应用三个层次。 目标:检验你能否把“读过的内容”变成“自己的武器”。
📄 试卷部分(请先独立完成,再对照答案)
第一部分:基础填空题(每题 2 分,共 20 分)
考察:核心定义是否准确记忆
- JVM 运行时数据区中,线程共享的区域是
______和______。 - 线程私有的区域包括
______、______和______。 - 程序计数器(Program Counter Register)是唯一一个在 Java 虚拟机规范中
______(会/不会)出现 OutOfMemoryError 的区域。 - 每个方法被调用时,JVM 都会同步创建一个
______,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 - 局部变量表中,64 位的数据类型(如 long、double)占用
______个变量槽(Variable Slot),其他类型占用 1 个。 - Java 堆是垃圾收集器管理的主要区域,因此也被称作
______。 - JDK 8 及以后,方法区的实现从“永久代”变为了
______(Metaspace)。 - 运行时常量池是
______的一部分,在类加载后存放 Class 文件中的常量池表信息。 - 通过
String.intern()方法可以在运行时将新的常量放入______中,这体现了运行时常量池的______特性。 - NIO 中使用的
______内存并不是虚拟机运行时数据区的一部分,但由于被频繁使用也可能导致内存溢出。
第二部分:概念辨析题(每题 5 分,共 20 分)
考察:能否区分容易混淆的概念
11. 虚拟机栈 vs 本地方法栈(5 分)
请用你自己的话,说清楚以下两个栈的本质区别:
| 对比维度 | 虚拟机栈 (VM Stack) | 本地方法栈 (Native Method Stack) |
|---|---|---|
| 服务对象 | ① | ② |
| 执行内容 | ③ | ④ |
| 在 HotSpot 中的关系 | ⑤ |
12. 方法区 vs 堆(5 分)
一个类 User 被加载后,以下内容分别存放在哪个区域?
new User()创建的对象实例 →______User类的字段信息、方法字节码 →______User类的静态变量static int count→______- 字符串常量
"hello"→______(JDK 8 及以后)
13. 栈帧(Stack Frame)的组成(5 分)
请列出栈帧中包含的 4 个核心组成部分,并简要说明每个部分的作用。
| 组成部分 | 作用说明 |
|---|---|
| ① | |
| ② | |
| ③ | |
| ④ |
14. 字面量 vs 符号引用(5 分)
请将以下内容归类到“字面量”或“符号引用”:
| 内容 | 分类(字面量 / 符号引用) |
|---|---|
"Hello World" | ① |
final int MAX = 100 中的 100 | ② |
java/lang/String 类的全限定名 | ③ |
方法名 sayHello() 及其描述符 ()V | ④ |
3.14f | ⑤ |
第三部分:场景应用题(每题 10 分,共 40 分)
考察:能否把知识应用到真实线上问题中
15. StackOverflowError 场景分析(10 分)
某天你的线上服务突然抛出 java.lang.StackOverflowError,日志显示堆栈深度达到 10000+ 层。
- 问题 1:这个异常最可能发生在 JVM 的哪个区域?(2 分)
- 问题 2:请写出 2 种最常见的代码场景会导致这个异常。(4 分)
- 问题 3:如果你在代码中使用了递归,应该如何避免这个异常?(4 分)
16. OutOfMemoryError: Java heap space 排查(10 分)
线上服务频繁出现 OutOfMemoryError: Java heap space,导致服务重启。
- 问题 1:这个异常发生在 JVM 的哪个区域?(1 分)
- 问题 2:请写出一个最简的 Java 代码示例,能重现这个 OOM。(3 分)
- 问题 3:假设你已经拿到了 Heap Dump 文件,你会使用什么工具、按什么步骤来排查是哪个对象占用了最多内存?(6 分)
17. 方法区 OOM 场景(10 分)
在 JDK 8 环境中,一个框架不停地在运行时动态生成新的类(例如某些 ORM 框架或动态代理框架),导致出现了 OutOfMemoryError: Metaspace。
- 问题 1:JDK 8 中这个异常对应的区域是哪里?(1 分)
- 问题 2:在 JDK 7 及以前,这个异常对应的区域叫什么?(1 分)
- 问题 3:如果要临时解决这个问题,你会加什么 JVM 参数来限制这个区域的大小?(2 分)
- 问题 4:从架构设计上,如何从根本上避免这个问题?(6 分)
18. 多线程与内存分布(10 分)
一个 Web 应用同时有 100 个用户在线,每个用户请求都会触发一次 UserService.getUserById() 调用。
- 问题 1:这 100 个请求在 JVM 内存中,会创建多少个栈帧?(假设每个请求方法调用深度一致)(3 分)
- 问题 2:这 100 个请求中,如果使用了
ThreadLocal存储用户上下文信息,ThreadLocal的数据是存放在哪个内存区域?它和栈是什么关系?(4 分) - 问题 3:程序计数器(PC)在每次线程切换时扮演什么角色?(3 分)
第四部分:综合论述题(20 分)
考察:能否把多个知识点串联成体系
19. 完整描述一个对象的“一生”(20 分)
请结合你学到的运行时数据区知识,完整描述一个对象从编写代码到运行时分配内存,再到被 GC 回收的全过程。
要求涵盖以下知识点:
- 对象在 方法区 中的“蓝图”(类信息)是如何来的?(4 分)
- 对象实例在 堆 中是如何分配内存的?(4 分)
- 方法调用时,栈帧 是如何创建和销毁的?(4 分)
- 程序计数器 在这个过程中发挥了什么作用?(4 分)
- GC 如何判定这个对象“已死”?(4 分)
💡 提示:可以用一个具体的例子,比如
User user = new User(); user.sayHello();,串联起所有区域。
🎯 评分标准与自我评估
| 分数区间 | 等级 | 说明 |
|---|---|---|
| 90 - 100 分 | 🏆 精通级 | 你不仅记住了概念,还能分析场景,第一天的学习效果远超预期! |
| 70 - 89 分 | ✅ 掌握级 | 核心知识已过关,部分细节(如符号引用 vs 字面量)需要再巩固。 |
| 50 - 69 分 | 📖 熟悉级 | 大框架没问题,但需要回头复习一下易混淆的细节(如栈帧组成)。 |
| < 50 分 | 🔄 需复习 | 建议用“费曼学习法”——假装教给别人,重新过一遍知识点。 |
📄 参考答案部分(做完后再看!)
第一部分:填空题答案
- Java堆、方法区
- 程序计数器、虚拟机栈、本地方法栈
- 不会
- 栈帧(Stack Frame)
- 2
- GC堆
- 元空间
- 方法区
- 运行时常量池、动态性
- 直接内存(Direct Memory)
第二部分:概念辨析题答案
11. 虚拟机栈 vs 本地方法栈
| 对比维度 | 虚拟机栈 (VM Stack) | 本地方法栈 (Native Method Stack) |
|---|---|---|
| 服务对象 | ① Java 方法 | ② Native(本地)方法 |
| 执行内容 | ③ Java 字节码 | ④ 本地语言(如 C/C++)代码 |
| 在 HotSpot 中的关系 | ⑤ 两者合二为一 |
12. 方法区 vs 堆
new User()→ 堆User类的字段信息、方法字节码 → 方法区(元空间)static int count→ 方法区(元空间)"hello"字符串常量 → 堆(JDK 8 字符串常量池在堆中)
13. 栈帧的 4 个核心组成部分
| 组成部分 | 作用说明 |
|---|---|
| ① 局部变量表 | 存储方法内的局部变量(基本类型、对象引用、返回地址) |
| ② 操作数栈 | 用于存储计算过程中的中间结果,类似 CPU 的寄存器 |
| ③ 动态链接 | 将符号引用转换为直接引用,支持多态(如 invokevirtual) |
| ④ 方法返回地址 | 记录方法执行完后应该返回到调用者的哪个位置继续执行 |
14. 字面量 vs 符号引用
| 内容 | 分类(字面量 / 符号引用) |
|---|---|
"Hello World" | ① 字面量 |
final int MAX = 100 中的 100 | ② 字面量 |
java/lang/String 类的全限定名 | ③ 符号引用 |
方法名 sayHello() 及其描述符 ()V | ④ 符号引用 |
3.14f | ⑤ 字面量 |
第三部分:场景应用题答案
15. StackOverflowError 场景分析
- 问题 1:虚拟机栈(或本地方法栈)区域。(2 分)
- 问题 2:① 无终止条件的递归调用(如
factorial()无限递归);② 方法之间循环调用(A 调用 B,B 调用 A)。(4 分) - 问题 3:① 设置递归终止条件;② 递归层数较多时改用循环(迭代)实现;③ 增大栈空间
-Xss(治标不治本)。(4 分)
16. OutOfMemoryError: Java heap space 排查
-
问题 1:Java 堆。(1 分)
-
问题 2:(3 分)
java
List<byte[]> list = new ArrayList<>();while (true) {list.add(new byte[1024 * 1024]); // 每次分配 1MB} -
问题 3:① 使用 Eclipse MAT 或 VisualVM 打开 Dump 文件;② 查看 Leak Suspects 报告,找到占用内存最大的对象;③ 查看该对象的 GC Roots 引用链,定位到是哪段业务代码持有引用;④ 修复代码(如释放引用、调整缓存大小)。(6 分)
17. 方法区 OOM 场景
- 问题 1:元空间(Metaspace)。(1 分)
- 问题 2:永久代(PermGen)。(1 分)
- 问题 3:
-XX:MaxMetaspaceSize=256m(限制元空间最大值)。(2 分) - 问题 4:① 使用类加载器缓存,避免重复加载相同类;② 框架层面限制动态代理类的生成数量;③ 及时卸载不再使用的类(需要确保类加载器可回收)。(6 分)
18. 多线程与内存分布
- 问题 1:100 个请求 × 每个请求的方法调用深度 = 100 × N 个栈帧(每个线程都有自己的虚拟机栈,栈帧数量 = 调用深度)。(3 分)
- 问题 2:
ThreadLocal的数据存储在 堆 中(实际上是ThreadLocalMap作为Thread对象的成员变量,而Thread对象在堆中)。栈中只存储ThreadLocal的引用。(4 分) - 问题 3:程序计数器(PC)记录当前线程下一步要执行的字节码指令地址。线程切换时,JVM 保存当前线程的 PC,恢复下一个线程的 PC,确保切换回来后能继续执行。(3 分)
第四部分:综合论述题参考框架(20 分)
19. 对象的“一生”参考回答
- 类加载(方法区)(4 分):JVM 通过 ClassLoader 加载
User.class文件,解析出类的字段、方法、常量等信息,存入方法区(元空间)。这一步产生了对象的“蓝图”。 - 对象分配(堆)(4 分):执行
new User()时,JVM 在 堆 中分配内存。分配方式包括指针碰撞(内存规整)或空闲列表(内存不规整),并初始化对象头(Mark Word)和实例数据。 - 栈帧创建(虚拟机栈)(4 分):调用
user.sayHello()时,在虚拟机栈中压入一个新的栈帧。栈帧中包含局部变量表(存储user引用)、操作数栈、动态链接和方法返回地址。方法执行完毕后,栈帧出栈销毁。 - 程序计数器参与(4 分):方法执行过程中,程序计数器始终指向下一条要执行的字节码指令的地址,保证方法按顺序执行。多线程切换时,PC 确保线程恢复到正确位置继续执行。
- GC 回收(堆)(4 分):当对象不再被任何 GC Roots(如栈中的局部变量、静态变量等)引用时,通过可达性分析判定对象“已死”。GC 会将其标记并清理,释放堆内存。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!