7. 2026金三银四 Java 虚拟机面试终极版:32 道必考题 + 图解 + 源码精讲

1 阅读15分钟

Q1【P0】JVM 运行时数据区域划分、线程私有/共享及各自作用

核心答案

  • 程序计数器:线程私有,记录当前字节码行号,唯一不出现 OOM。
  • 虚拟机栈:线程私有,存放栈帧(局部变量表、操作数栈、动态链接、方法出口),深度溢出抛 StackOverflowError
  • 本地方法栈:线程私有,为 Native 方法服务。
  • :线程共享,存放所有对象实例和数组,GC 主战场,可能 OOM。
  • 方法区:线程共享,存储类元数据、常量池、静态变量、方法信息。JDK8 后由**元空间(Metaspace)**实现,使用本地内存。

🔁 Mermaid 流程图

graph TB
    subgraph 线程私有
        PC[程序计数器]
        VMS[虚拟机栈] --> SF[栈帧]
        NMS[本地方法栈]
    end
    subgraph 线程共享
        Heap[堆] --> Obj[对象实例]
        MA[方法区/元空间] --> Meta[类信息/常量/静态变量]
    end

🧩 精简源码(栈溢出示例)

public class StackOverflowDemo {
    public static void recursive() { recursive(); }
    public static void main(String[] args) {
        recursive(); // 抛出 StackOverflowError
    }
}

Q2【P0】JVM 堆内存结构划分、新生代/老年代比例及对象分配策略

核心答案

  • 堆结构:新生代(1/3) + 老年代(2/3)。
  • 新生代内部分配:Eden : Survivor From : Survivor To = 8 : 1 : 1。
  • 分配流程
    1. 新对象优先在 Eden 区分配。
    2. Eden 满 → Minor GC → 存活对象移入空闲 Survivor。
    3. 对象每经历一次 Minor GC 年龄+1,默认 15 晋升老年代。
    4. 大对象(如长数组)直接进入老年代。
    5. 老年代满 → Full GC。

🔁 Mermaid 流程图

graph TB
    New[新对象] --> Eden[Eden区]
    Eden -->|满| MinorGC[Minor GC]
    MinorGC --> Survivor[存活对象→Survivor To]
    Survivor -->|年龄≥15| Old[晋升老年代]
    Old -->|满| FullGC[Full GC]

🧩 精简源码

public class HeapAllocTest {
    public static void main(String[] args) {
        while (true) {
            byte[] temp = new byte[1024 * 1024];
        }
    }
}
// VM参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails

Q3【P0】GC 可达性分析算法原理及废弃引用计数的原因

核心答案

  • 可达性分析:从 GC Roots 出发遍历引用链,不可达对象判定为可回收。
  • GC Roots 包含:栈引用、静态属性/常量引用、JNI 引用、活跃线程等。
  • 引用计数缺陷:无法解决循环引用(如 A→B→A,外部无引用但计数器不为 0),JVM 废弃。

🔁 Mermaid 流程图

graph TB
    GC[GC Roots] --> A[对象A]
    A --> B[对象B]
    B -.->|循环引用| A
    C[对象C] -.->|无引用链| D[可回收]

🧩 精简源码(循环引用可回收)

class Node { Node next; }
public class CircleRefDemo {
    public static void main(String[] args) {
        Node a = new Node();
        Node b = new Node();
        a.next = b; b.next = a;
        a = null; b = null;
        System.gc(); // 可达性分析可正常回收
    }
}

Q4【P0】四种引用类型:强/软/弱/虚引用的区别、回收时机及应用场景

核心答案

  • 强引用:默认引用,永不回收(即使 OOM)。
  • 软引用:内存不足时回收,适用缓存(图片、大对象)。
  • 弱引用:下一次 GC 必回收,适用防止内存泄漏(如 Handler 持有 Activity)。
  • 虚引用:无法获取对象,仅用于监控回收(配合 ReferenceQueue),适用堆外内存释放(如 DirectBuffer)。

🔁 Mermaid 流程图

graph LR
    Strong[强引用] -->|永不回收| Soft[软引用] -->|内存不足回收| Weak[弱引用] -->|每次GC回收| Phantom[虚引用]

🧩 精简源码

import java.lang.ref.*;
public class RefDemo {
    public static void main(String[] args) {
        Object strong = new Object();
        SoftReference<Object> soft = new SoftReference<>(new Object());
        WeakReference<Object> weak = new WeakReference<>(new Object());
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        PhantomReference<Object> phantom = new PhantomReference<>(new Object(), queue);
    }
}

Q5【P0】类加载的完整生命周期(7个阶段)及每阶段工作

核心答案

  1. 加载:读取 class 二进制流,生成 Class 对象。
  2. 验证:校验字节码合法性。
  3. 准备:为静态变量分配内存并赋默认零值。
  4. 解析:将符号引用替换为直接引用。
  5. 初始化:执行静态代码块,为静态变量赋自定义值。
  6. 使用:实例化对象、调用方法。
  7. 卸载:类加载器回收后,卸载类元数据。

🔁 Mermaid 流程图

graph LR
    Load[加载] --> Verify[验证] --> Prepare[准备] --> Resolve[解析] --> Init[初始化] --> Use[使用] --> Unload[卸载]

🧩 精简源码(观察初始化阶段)

public class ClassInitDemo {
    static { System.out.println("静态代码块执行,即初始化阶段"); }
    public static void main(String[] args) {
        // 触发初始化
    }
}

Q6【P0】双亲委派模型原理、执行流程及设计目的

核心答案

  • 原理:类加载器收到请求后,先委派给父加载器,层层向上到启动类加载器;父加载器无法加载时,才由子加载器自己加载。
  • 流程:自定义 → 应用类加载器 → 扩展类加载器 → 启动类加载器。
  • 目的:避免重复加载类、防止核心类库被篡改(沙箱安全)、保证类优先级。

🔁 Mermaid 流程图

graph TD
    Custom[自定义类加载器] -->|委托| App[应用类加载器]
    App -->|委托| Ext[扩展类加载器]
    Ext -->|委托| Boot[启动类加载器]
    Boot -->|无法加载| Ext -->|无法加载| App -->|自己加载| Custom

🧩 精简源码(自定义类加载器)

public class CustomClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 遵循双亲委派
        return super.loadClass(name);
    }
}

Q7【P0】Minor GC、Major GC、Full GC 的区别及触发时机

核心答案

  • Minor GC:只回收新生代,频繁快速,Eden 满触发。
  • Major GC:只回收老年代(较少单独出现)。
  • Full GC:回收整个堆 + 方法区/元空间,耗时最长。触发条件:老年代空间不足、手动 System.gc()、Minor GC 后晋升对象放不下、元空间满。

🔁 Mermaid 流程图

graph TD
    Eden满 --> MinorGC[Minor GC]
    Old满 --> FullGC[Full GC]
    System.gc() --> FullGC
    Metaspace满 --> FullGC

🧩 精简源码

public class GcTriggerDemo {
    public static void main(String[] args) {
        System.gc(); // 建议JVM执行Full GC
    }
}

Q8【P0】JVM 三大垃圾回收算法:复制、标记-清除、标记-整理的原理与适用场景

核心答案

算法原理优点缺点适用区域
复制存活对象复制到空闲区,清空原区无碎片,简单内存浪费(双倍空间)新生代
标记-清除标记垃圾后直接清除快,不移动对象产生内存碎片老年代(CMS)
标记-整理标记后存活对象向一端移动,清理边界无碎片,紧凑移动对象开销大老年代(Serial Old)

🔁 Mermaid 流程图(以标记-整理为例)

graph LR
    Before[标记前:存活+垃圾] --> Mark[标记存活对象] --> Compact[向左移动存活对象] --> After[清理边界:无碎片]

🧩 精简源码

算法为 JVM 内置,无特定代码;可通过 GC 日志观察。


Q9【P0】JDK8 废弃永久代改用元空间的原因

核心答案

  • 永久代大小固定,容易 OOM(尤其动态生成类场景)。
  • 元空间使用本地物理内存,不受 JVM 堆限制,默认无上限。
  • 类元数据、常量池、方法信息移入元空间,减少 Full GC 频率。
  • 方便热部署、插件化、动态代理等场景。

🔁 Mermaid 流程图

graph LR
    JDK7[JDK7:永久代 堆内] -->|JDK8| JDK8[JDK8:元空间 本地内存]

🧩 精简源码

无法代码演示,属于 JVM 底层内存布局变更。


Q10【P0】Android 中常见 JVM 内存泄漏的本质与典型场景

核心答案

  • 本质:本应被回收的对象,被 GC Roots 强引用链持有,导致无法回收。
  • 典型场景
    1. 静态变量长期持有 Activity/View/Context。
    2. 匿名内部类(如 Handler、AsyncTask)隐式持有外部类。
    3. 全局集合(ArrayList、HashMap)未清空。
    4. 资源未关闭:Cursor、FileInputStream、Bitmap。
    5. 单例模式中持有 Activity 上下文而非 Application 上下文。

🔁 Mermaid 流程图

graph TD
    GC[GC Roots] -->|强引用| Leak[无辜对象] -->|无法回收| MemLeak[内存泄漏] --> OOM

🧩 精简源码(静态持有 Activity)

public class StaticLeakDemo {
    private static Object sHold;
    public static void hold(Object obj) { sHold = obj; }
}

Q11【P0】JVM 虚拟机栈的栈帧组成

核心答案

每个栈帧包含:

  • 局部变量表:存储方法参数和局部变量(以 slot 为单位)。
  • 操作数栈:存放计算过程中的中间结果。
  • 动态链接:指向运行时常量池中该方法的符号引用。
  • 方法返回地址:正常或异常退出后的返回点。
  • 附加信息:调试信息等。

🔁 Mermaid 流程图

graph TB
    subgraph 虚拟机栈
        Frame3[栈帧3 方法C]
        Frame2[栈帧2 方法B]
        Frame1[栈帧1 方法A]
    end
    subgraph 栈帧详细结构
        LV[局部变量表]
        OS[操作数栈]
        DL[动态链接]
        RA[返回地址]
    end
    Frame1 --> LV & OS & DL & RA

🧩 精简源码(通过 javap 查看)

public class StackFrameDemo {
    public static void methodA(int a, int b) { int c = a + b; methodB(c); }
    private static void methodB(int sum) { System.out.println(sum); }
    public static void main(String[] args) { methodA(1,2); }
}
// 命令:javap -c -v StackFrameDemo.class

Q12【P0】JVM 对象创建的完整过程(从 new 到初始化)

核心答案

  1. 检查类是否已加载、链接、初始化。
  2. 分配内存(指针碰撞或空闲列表)。
  3. 初始化对象头(MarkWord、类型指针)。
  4. 实例数据赋默认零值。
  5. 执行 <init> 构造方法,按程序赋值。

🔁 Mermaid 流程图

graph TD
    New[new 对象] --> Check[类加载校验]
    Check --> Alloc[分配堆内存]
    Alloc --> Header[初始化对象头]
    Header --> Zero[实例数据默认零值]
    Zero --> Init[执行构造方法]

🧩 精简源码

public class ObjectCreateDemo {
    public static void main(String[] args) {
        User user = new User(); // 触发完整创建流程
    }
}
class User { int id; String name; }

Q13【P1】CMS 垃圾收集器的四步执行流程及优缺点

核心答案

  • 流程:初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除。
  • 优点:大部分阶段与用户线程并发,低停顿。
  • 缺点:标记-清除产生碎片;无法处理浮动垃圾;占用 CPU 高。

🔁 Mermaid 流程图

graph LR
    InitMark[初始标记 STW] --> ConMark[并发标记] --> Remark[重新标记 STW] --> ConSweep[并发清除]

🧩 精简源码(指定 CMS 参数)

-XX:+UseConcMarkSweepGC

Q14【P1】G1 垃圾收集器的核心原理与 Region 分区概念

核心答案

  • 将堆划分为多个大小相等的 Region,不分固定新生代/老年代。
  • 追踪每个 Region 的垃圾堆积价值,优先回收垃圾最多的 Region。
  • 可实现可预测停顿,兼顾吞吐量与低延迟。
  • JDK9+ 默认垃圾收集器。

🔁 Mermaid 流程图

graph TB
    Heap[堆内存] --> R1[Region Eden]
    Heap --> R2[Region Survivor]
    Heap --> R3[Region Old]
    Heap --> R4[Region Humongous]
    R1 & R2 & R3 & R4 --> GC[优先回收垃圾最多的Region]

🧩 精简源码

-XX:+UseG1GC

Q15【P1】JVM 对象的内存布局(对象头、实例数据、对齐填充)

核心答案

  • 对象头:MarkWord(哈希码、GC 年龄、锁状态) + 类型指针(指向方法区类元信息)。
  • 实例数据:对象中各个字段的值。
  • 对齐填充:保证对象大小是 8 字节的整数倍。

🔁 Mermaid 流程图

graph LR
    Header[对象头] --> Instance[实例数据] --> Padding[对齐填充]
    subgraph Header内容
        Mark[MarkWord]
        Type[类型指针]
    end

🧩 精简源码(使用 jol 查看)

// 依赖:org.openjdk.jol:jol-core
public class ObjectLayoutDemo {
    private int id;
    public static void main(String[] args) {
        ObjectLayoutDemo obj = new ObjectLayoutDemo();
        // System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}

Q16【P1】逃逸分析是什么?它能带来哪些优化?

核心答案

  • 逃逸分析:分析对象的作用域是否会逃逸出当前方法或线程。
  • 优化手段
    • 栈上分配:未逃逸对象直接在栈上分配,避免堆分配和 GC。
    • 标量替换:将对象字段拆分为基本类型,存储在栈上。
    • 同步消除:未逃逸对象的锁可以消除(本合集已剔除锁相关内容,仅提及)。
  • 效果:降低堆压力,提升性能。

🔁 Mermaid 流程图

graph TD
    Obj[创建对象] --> Escape{逃逸分析}
    Escape -->|未逃逸| Stack[栈上分配/标量替换]
    Escape -->|已逃逸| Heap[堆分配]

🧩 精简源码

public class EscapeDemo {
    public void test() {
        Point p = new Point(1, 2); // 未逃逸,可栈上分配
        System.out.println(p.x);
    }
    class Point { int x,y; Point(int x,int y){this.x=x;this.y=y;} }
}
// VM参数:-XX:+DoEscapeAnalysis -XX:+PrintEscapeAnalysis

Q17【P1】Class 文件常量池、运行时常量池、字符串常量池的区别

核心答案

  • Class 常量池:编译期生成,存在于 .class 文件中,存字面量和符号引用。
  • 运行时常量池:类加载后移入方法区(元空间),支持动态添加(如 String.intern())。
  • 字符串常量池:JDK7+ 位于堆中,专门缓存字符串字面量,避免重复创建。

🔁 Mermaid 流程图

graph LR
    ClassFile[.class文件常量池] -->|类加载| Runtime[运行时常量池 方法区]
    StringLiteral[字符串字面量] -->|intern| StringPool[字符串常量池 堆]

🧩 精简源码

public class ConstantPoolDemo {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = new String("hello");
        System.out.println(s1 == s2.intern()); // true
    }
}

Q18【P1】Android ART 虚拟机与标准 JVM 的核心区别

核心答案

特性JVM(HotSpot)Android ART
执行方式解释执行 + JITAOT 预编译(5.0-6.0);混合模式(7.0+: 解释+JIT+后台AOT)
字节码.class.jar.dex.oat / .odex
GC 算法分代收集(CMS/G1/ZGC)并发复制(Concurrent Copying)+ 标记清除
启动速度较慢更快
内存占用相对较低略高(AOT 后存储空间)

🔁 Mermaid 流程图

graph LR
    subgraph JVM
        Dex1[.dex] --> Interpret[解释执行] --> JIT[JIT编译]
    end
    subgraph ART
        Dex2[.dex] --> AOT[安装期AOT编译] --> Native[本地机器码运行]
    end

🧩 精简源码(Android 中观察 ART GC)

System.gc(); // logcat 过滤 "art" 查看 GC 信息

Q19【P1】JVM 类初始化的触发时机及不触发的情况

核心答案

  • 触发new 实例、调用静态方法、访问静态非常量字段、反射 Class.forName()、子类初始化触发父类。
  • 不触发:仅声明引用(如 Person p = null;)、访问静态常量(编译期常量)、通过数组引用类。

🔁 Mermaid 流程图

graph TD
    Trigger[触发初始化] --> New[new实例]
    Trigger --> StaticMethod[调用静态方法]
    Trigger --> StaticField[访问静态非常量字段]
    Trigger --> Reflect[反射 Class.forName]
    Trigger --> SubClass[子类初始化]
    
    NoTrigger[不触发初始化] --> Declare[仅声明变量]
    NoTrigger --> Constant[访问静态常量]
    NoTrigger --> ArrayRef[数组引用]

🧩 精简源码

public class InitTriggerDemo {
    public static void main(String[] args) {
        Person p = null;      // 不会触发初始化
        System.out.println(Person.CONST); // 若CONST是编译期常量,也不触发
    }
}
class Person {
    static { System.out.println("初始化"); }
    static final int CONST = 100;
}

Q20【P1】JVM 类卸载需要满足的条件

核心答案

  1. 该类的所有实例已被回收。
  2. 加载该类的 Class 对象没有任何引用。
  3. 加载该类的类加载器已被回收。

三个条件同时满足时,类元数据才会被卸载。

🔁 Mermaid 流程图

graph LR
    AllInst[所有实例已回收] --> ClassRef[Class无引用] --> Loader[类加载器已回收] --> Unload[类卸载]

🧩 精简源码(通常需结合自定义 ClassLoader 演示)

// 示例难以直接展示,需配合自定义类加载器并清空引用后调用 GC

Q21【P1】Stop-The-World(STW)的概念、触发事件及减少方法

核心答案

  • STW:GC 时暂停所有应用线程。
  • 触发 STW 的 GC 事件:Minor GC、Full GC、CMS 初始标记和重新标记、G1 初始标记和最终标记。
  • 减少方法:使用并发收集器(CMS/G1)、调整新生代大小、减少 Full GC 频率(避免大对象、合理设置元空间)。

🔁 Mermaid 流程图

graph LR
    App[应用线程运行] --> GC[触发GC] --> STW[全部暂停 STW] --> Execute[执行GC] --> Resume[恢复所有线程]

🧩 精简源码(通过 JVM 参数观察)

-XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics

Q22【P1】SafePoint(安全点)和 SafeRegion(安全区域)的作用

核心答案

  • SafePoint:程序执行中的特定位置(如循环末尾、方法返回前),线程在此处暂停以执行 GC 等 VM 操作。
  • SafeRegion:一段引用关系不会变化的代码区域(如线程处于 Sleep/Blocked 状态),GC 可安全忽略该线程。
  • 作用:保证 GC 时所有线程能快速到达一致状态,避免扫描不稳定的栈。

🔁 Mermaid 流程图

graph TD
    Code[代码执行] --> Check{是否达到SafePoint?}
    Check -->|是| Pause[暂停线程] --> GC --> Resume[恢复]
    Check -->|否| Continue[继续执行]

🧩 精简源码(通过 JVM 参数打印安全点停顿)

-XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics

Q23【P1】JVM 参数 -Xms/-Xmx/-Xmn/-XX:MetaspaceSize 含义及 Android 堆设置

核心答案

  • -Xms:堆初始大小。
  • -Xmx:堆最大大小。
  • -Xmn:新生代大小(老年代 = 堆 - 新生代)。
  • -XX:MetaspaceSize:元空间触发 Full GC 的阈值。
  • Android 设置:在 AndroidManifest.xml 中声明 android:largeHeap="true" 请求更大堆(具体值因设备而异)。

🔁 Mermaid 流程图

graph TD
    Heap[堆] --> Young[新生代 -Xmn] --> Eden[Eden]
    Young --> S0[Survivor0]
    Young --> S1[Survivor1]
    Heap --> Old[老年代]
    Meta[元空间] -->|本地内存| NativeMemory

🧩 精简源码(Android 大堆)

<application android:largeHeap="true">

Q24【P1】GC Roots 包含哪些?请列举至少 4 种

核心答案

  1. 虚拟机栈(局部变量表)中引用的对象。
  2. 方法区中静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. JNI(Native 方法)引用的对象。
  5. 活跃线程(Thread 对象)。
  6. 同步监视器(synchronized 锁对象,本合集已剔除但可作为扩展)。

🔁 Mermaid 流程图

graph TD
    GC[GC Roots] --> Stack[栈引用]
    GC --> Static[静态属性引用]
    GC --> Constant[常量引用]
    GC --> JNI[JNI引用]
    GC --> Thread[活跃线程]

🧩 精简源码(验证 GC Roots)

public class GCRootsDemo {
    private static Object staticObj = new Object();   // 静态引用
    private static final Object constObj = new Object(); // 常量引用
    public void method() {
        Object localObj = new Object();   // 栈引用
        System.gc(); // 这三者均不会被回收
    }
}

Q25【P2】JVM 内存溢出(OOM)的常见触发场景

核心答案

  • 堆 OOM:不断创建大对象、集合无限添加、大量 Bitmap。
  • 元空间 OOM:动态生成大量类(如热修复、插件化、动态代理)。
  • 栈 OOM:递归过深或线程数量过多(线程过多导致栈内存不足)。
  • 堆外内存 OOM:Native ByteBuffer、未释放的 Bitmap 像素内存。

🔁 Mermaid 流程图

graph TD
    HeapOOM[堆OOM] --> BigObj[大对象/集合无限]
    MetaOOM[元空间OOM] --> DynamicClass[动态生成大量类]
    StackOOM[栈OOM] --> DeepRecursion[递归深度过大]
    NativeOOM[堆外OOM] --> BitmapUnreleased[Bitmap未释放]

🧩 精简源码(堆 OOM 示例)

import java.util.ArrayList;
public class HeapOOM {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]); // 不断分配1MB数组
        }
    }
}
// VM参数:-Xms10M -Xmx10M

Q26【P2】符号引用与直接引用的区别?解析阶段做了什么?

核心答案

  • 符号引用:编译期字符串形式描述类、方法、字段,无实际内存地址。
  • 直接引用:运行时真实内存指针、偏移地址。
  • 解析阶段:将符号引用替换为直接引用,绑定真实内存地址。

🔁 Mermaid 流程图

graph LR
    Symbol[符号引用 字符串] --> Resolve[解析阶段] --> Direct[直接引用 内存地址/偏移]

🧩 精简源码(概念性)

// Java 代码中的方法调用在 class 文件中存为符号引用,运行时转换为直接引用

Q27【P2】JVM 内存分配方式:指针碰撞与空闲列表的区别

核心答案

  • 指针碰撞:堆内存规整时,用指针标记分配位置,分配时向后移动指针。效率高。
  • 空闲列表:堆内存有碎片时,维护空闲内存列表,分配时遍历找到合适空间。

🔁 Mermaid 流程图

graph TD
    Compact[内存规整] --> Pointer[指针碰撞 简单移动指针]
    Fragmented[内存碎片] --> FreeList[空闲列表 遍历查找]

Q28【P2】字符串 intern() 方法的底层常量池复用逻辑

核心答案

  • intern():将字符串放入字符串常量池,如果池中已存在则返回池中引用,否则存入并返回。
  • 作用:复用字符串对象,节省内存,减少重复创建。

🔁 Mermaid 流程图

graph TD
    Intern[调用 intern] --> Check{常量池中是否存在?}
    Check -->|存在| Return[返回池中引用]
    Check -->|不存在| Add[加入常量池] --> Return2[返回新引用]

🧩 精简源码

public class InternDemo {
    public static void main(String[] args) {
        String s1 = new String("abc").intern();
        String s2 = "abc";
        System.out.println(s1 == s2); // true
    }
}

Q29【P2】Android 中 Bitmap 内存占用与 JVM 堆外内存的关系

核心答案

  • 高版本 Android(8.0+)中,Bitmap 的像素数据存放在堆外 Native 内存,不在 JVM 堆内。
  • JVM 堆中只保留 Bitmap 对象头引用。
  • 堆外内存需要主动调用 recycle() 释放,否则会导致 Native 内存泄漏,同样引发 OOM。

🔁 Mermaid 流程图

graph LR
    subgraph JVM堆
        BitmapObj[Bitmap对象 引用]
    end
    subgraph Native堆
        PixelData[像素数据]
    end
    BitmapObj --> PixelData

🧩 精简源码(模拟 Bitmap 使用)

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.large);
// 使用完毕后需及时释放
bitmap.recycle();

Q30【P2】JVM 方法区(元空间)中具体存放哪些内容?

核心答案

  • 类版本信息、字段信息、方法信息。
  • 运行时常量池。
  • 静态变量(JDK7 后移到堆,但逻辑上属于方法区)。
  • 即时编译器(JIT)编译后的代码缓存。
  • 注解元数据。

🔁 Mermaid 流程图

graph TB
    Meta[方法区/元空间] --> ClassInfo[类结构信息 版本/字段/方法]
    Meta --> RuntimePool[运行时常量池]
    Meta --> Static[静态变量 逻辑上]
    Meta --> JITCode[JIT编译代码缓存]
    Meta --> Annotation[注解元数据]

Q31【P2】新生代为什么不用标记-清除或标记-整理算法?

核心答案

  • 新生代对象存活率极低(约 98% 以上一次性回收),复制算法成本最低(只需复制少量存活对象)。
  • 标记-清除会产生碎片,新生代频繁分配回收会导致碎片严重。
  • 标记-整理需要移动大量存活对象,开销远大于复制算法。

🔁 Mermaid 流程图

graph LR
    Young[新生代 存活率低] --> Copy[复制算法 最优]
    Old[老年代 存活率高] --> MarkCompact[标记-整理 更合适]

Q32【P2】JVM 启动类加载器、扩展类加载器、应用类加载器的职责

核心答案

  • 启动类加载器(Bootstrap):加载 JDK 核心类库(rt.jar 等),C++ 实现,无父加载器。
  • 扩展类加载器(Extension):加载 jre/lib/ext 下的扩展 jar 包。
  • 应用类加载器(Application):加载 CLASSPATH 下的项目类和第三方依赖。

🔁 Mermaid 流程图

graph TD
    Boot[启动类加载器] -->|父| Extension[扩展类加载器]
    Extension -->|父| App[应用类加载器]
    App -->|父| Custom[自定义类加载器]

🧩 精简源码(打印类加载器层级)

public class ClassLoaderHierarchy {
    public static void main(String[] args) {
        ClassLoader cl = ClassLoaderHierarchy.class.getClassLoader();
        while (cl != null) {
            System.out.println(cl);
            cl = cl.getParent();
        }
    }
}