JVM核心知识点高频面试题(含Java代码案例

JVM核心知识点高频面试题(含Java代码案例)

一、JVM内存区域相关

面试题1:JVM运行时内存区域如何划分?线程私有与共享区域有哪些区别?

核心知识点

根据文档内容,JVM运行时内存区域分为线程私有区域线程共享区域直接内存三大类,具体划分及特性如下:

  • 线程私有区域:生命周期与线程一致,随线程创建/销毁而创建/销毁,避免线程间数据竞争。
    1. 程序计数器:记录当前线程执行的字节码地址,执行Native方法时为null,是唯一无OutOfMemoryError的区域。
    2. 虚拟机栈:描述Java方法执行的内存模型,每个方法对应一个栈帧(存储局部变量表、操作数栈等),栈深度超限抛StackOverflowError,动态扩展失败抛OutOfMemoryError
    3. 本地方法栈:为Native方法服务,HotSpot VM将其与虚拟机栈合二为一,异常类型同虚拟机栈。
  • 线程共享区域:随虚拟机启动/关闭而创建/销毁,供所有线程共享访问。
    1. 堆(Heap):存储对象和数组,是GC的主要区域,按GC角度分为新生代(Eden、From Survivor、To Survivor)和老年代。
    2. 方法区(元空间):存储类信息、常量、静态变量等,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,引用失效时-1,计数器为0则对象可回收。
    • 缺陷:无法解决循环引用问题(如A引用B,B引用A,两者计数器均为1,但实际已无用),因此Java未采用。
  2. 可达性分析(主流算法)
    • 原理:以“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()
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值