JVM核心知识点高频面试题(含Java代码案例)
一、JVM内存区域相关
面试题1:JVM运行时内存区域如何划分?线程私有与共享区域有哪些区别?
核心知识点
根据文档内容,JVM运行时内存区域分为线程私有区域、线程共享区域和直接内存三大类,具体划分及特性如下:
- 线程私有区域:生命周期与线程一致,随线程创建/销毁而创建/销毁,避免线程间数据竞争。
- 程序计数器:记录当前线程执行的字节码地址,执行Native方法时为null,是唯一无
OutOfMemoryError的区域。 - 虚拟机栈:描述Java方法执行的内存模型,每个方法对应一个栈帧(存储局部变量表、操作数栈等),栈深度超限抛
StackOverflowError,动态扩展失败抛OutOfMemoryError。 - 本地方法栈:为Native方法服务,HotSpot VM将其与虚拟机栈合二为一,异常类型同虚拟机栈。
- 程序计数器:记录当前线程执行的字节码地址,执行Native方法时为null,是唯一无
- 线程共享区域:随虚拟机启动/关闭而创建/销毁,供所有线程共享访问。
- 堆(Heap):存储对象和数组,是GC的主要区域,按GC角度分为新生代(Eden、From Survivor、To Survivor)和老年代。
- 方法区(元空间):存储类信息、常量、静态变量等,Java8前为永久代,Java8后替换为元空间(使用本地内存,默认不受JVM内存限制)。
- 直接内存:非JVM运行时数据区一部分,通过NIO的
DirectByteBuffer分配堆外内存,避免堆与Native堆的数据拷贝,可提升性能,但分配过多会抛OutOfMemoryError。
代码案例1:虚拟机栈StackOverflowError演示
/**
* 虚拟机栈异常:栈深度超过JVM限制,抛StackOverflowError
* 文档对应:线程私有区域-虚拟机栈的StackOverflowError异常
*/
public class StackOverflowDemo {
private int stackDepth = 0;
// 递归调用导致栈帧不断入栈,超出栈深度限制
public void recursiveCall() {
stackDepth++;
recursiveCall();
}
public static void main(String[] args) {
StackOverflowDemo demo = new StackOverflowDemo();
try {
demo.recursiveCall();
} catch (StackOverflowError e) {
System.out.println("栈深度:" + demo.stackDepth);
e.printStackTrace();
}
}
}
代码案例2:堆OutOfMemoryError演示
import java.util.ArrayList;
import java.util.List;
/**
* 堆内存溢出:对象数量超出堆容量限制,抛OutOfMemoryError
* 文档对应:线程共享区域-堆的内存分配与OOM
* 运行参数:-Xms20m -Xmx20m(限制堆最大/初始容量为20MB)
*/
public class HeapOOMDemo {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
// 循环创建对象,直至堆内存耗尽
while (true) {
list.add(new OOMObject());
}
}
}
面试题2:程序计数器的作用是什么?为什么它是唯一无OOM的内存区域?
核心知识点
- 作用:程序计数器是线程私有区域,用于记录当前线程执行的字节码指令地址。当线程执行Java方法时,计数器存储虚拟机字节码指令的地址;当执行Native方法时,计数器值为null(因Native方法由操作系统实现,JVM无法跟踪)。
- 无OOM原因:程序计数器的内存占用空间极小(仅存储指令地址),且JVM对其容量有严格控制,不会因动态扩展或深度超限导致内存不足,因此是JVM规范中唯一未规定
OutOfMemoryError的区域(文档1-87)。 - 线程私有必要性:多线程切换时,需通过程序计数器恢复当前线程的执行位置,确保线程切换后能继续执行,因此必须为每个线程分配独立的程序计数器。
二、垃圾回收(GC)相关
面试题3:JVM如何判断对象可回收?常见的对象存活判定算法有哪些?
核心知识点
JVM判断对象可回收的核心是“对象是否再被使用”,主要通过两种算法实现(文档1-139):
- 引用计数法:
- 原理:为每个对象维护一个引用计数器,有引用指向对象时计数器+1,引用失效时-1,计数器为0则对象可回收。
- 缺陷:无法解决循环引用问题(如A引用B,B引用A,两者计数器均为1,但实际已无用),因此Java未采用。
- 可达性分析(主流算法):
- 原理:以“GC Roots”为起点,向下搜索可达路径(引用链),无引用链连接的对象为“不可达对象”(需经历两次标记才会被回收)。
- GC Roots包含的对象(文档1-143):
- 虚拟机栈中局部变量表的引用对象;
- 本地方法栈中Native方法的引用对象;
- 方法区中类静态属性的引用对象;
- 方法区中常量的引用对象;
- 活跃线程的引用对象。
代码案例:可达性分析中对象可回收演示
/**
* 演示可达性分析:不可达对象可被GC回收
* 文档对应:可达性分析算法与GC Roots
*/
public class ReachabilityAnalysisDemo {
static class Node {
Node next; // 引用其他Node对象
}
public static void main(String[] args) {
Node a = new Node()

最低0.47元/天 解锁文章
86万+

被折叠的 条评论
为什么被折叠?



