jvm

4 阅读8分钟

1、什么是 JVM

答案

JVM 是 Java 虚拟机,是运行 Java 字节码的虚拟机器。作用:

  • .class 字节码翻译成机器指令执行
  • 实现 一次编写,到处运行 跨平台
  • 负责 内存管理、垃圾回收、类加载、即时编译、安全校验

2、JVM 运行时数据区 5 大内存区域(必背)

答案

  1. 程序计数器:记录当前线程执行字节码行号,唯一没有 OOM的区域。
  2. 虚拟机栈(Java 栈) :方法执行、局部变量、栈帧,线程私有。
  3. 本地方法栈:给 Native 方法服务,JNI 调用。
  4. 堆 Heap:所有对象实例、数组,GC 主要回收区域,线程共享。
  5. 方法区:存放类结构、常量、静态变量、运行时常量池、元数据。

3、程序计数器作用

答案

记录线程当前执行到第几行字节码;

线程切换后恢复执行位置

是 JVM 唯一不会内存溢出的内存区域。

4、虚拟机栈 栈帧是什么

答案

每调用一个方法就创建一个栈帧,方法结束栈帧出栈。栈帧包含:

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法返回地址

5、堆内存结构划分(新生代 / 老年代)

答案堆分两大块:

  1. 新生代 Young

    • Eden 伊甸区
    • Survivor From
    • Survivor To
  2. 老年代 Old

默认比例:Eden : From : To = 8:1:1

6、对象创建流程

答案

  1. 类加载检查
  2. 分配内存(指针碰撞 / 空闲列表)
  3. 初始化默认值
  4. 设置对象头
  5. 执行构造方法
  6. 返回对象引用

7、GC 垃圾回收 哪些对象可回收

答案

  1. 没有任何强引用指向的对象
  2. 引用计数为 0
  3. 可达性分析中不在 GC Root 引用链

8、GC Root 有哪些? 为什么要 GC Root?

答案

GC Root 是垃圾回收的起始引用点,从 Root 往下遍历,找不到的对象就是垃圾。

常见GC Root:

  • 虚拟机栈中引用的对象
  • 本地方法栈 JNI 引用对象
  • 方法区静态变量引用
  • 常量池引用
  • 活跃线程持有的对象

9、四种引用类型(强 / 软 / 弱 / 虚)

答案

  1. 强引用:正常 new,GC 永不回收
  2. 软引用 SoftReference:内存不足才回收,适合缓存
  3. 弱引用 WeakReference:下次 GC 必回收,适合 LeakCanary、生命周期监听
  4. 虚引用 PhantomReference:仅做回收通知,不能拿对象

10、Minor GC、Major GC、Full GC 区别

答案

  • Minor GC:只回收新生代,频率高、速度快
  • Major GC:只回收老年代
  • Full GC:整堆回收(新生代 + 老年代 + 方法区),耗时高、尽量避免

11、垃圾回收算法 4 种

答案

  1. 标记清除:先标记再清除,产生内存碎片
  2. 复制算法:新生代用,分成两块,存活复制到另一块,无碎片
  3. 标记整理算法:老年代用,标记后向一端压缩整理,无碎片
  4. 分代收集:新生代复制、老年代标记整理

12、常见垃圾收集器

答案

  • Serial:单线程收集器
  • Parallel:多线程吞吐量优先
  • CMS:并发低停顿,老年代
  • G1:分区收集,可预测停顿
  • ZGC、Shenandoah:超低延迟大堆收集器

13、CMS 垃圾回收流程

答案

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清除

特点:并发回收、低停顿,有浮动垃圾、内存碎片。

14、JVM 内存溢出 OOM 常见场景

答案

  1. 堆内存溢出:对象过多、大集合不释放
  2. 虚拟机栈溢出:递归死循环
  3. 方法区溢出:动态生成大量类
  4. 直接内存溢出:NIO 未释放缓冲区

15、调用 System.gc() 会立即垃圾回收吗?

不会立即执行,只是「建议」JVM 回收,不保证马上 GC,也不保证一定回收。

调用 System.gc() 只是向 JVM 建议垃圾回收,不会立即执行、也不保证一定执行,JVM 会在合适的安全点自行决定何时 GC,开发者不能强制立即回收。

16、可达性分析原理是什么?

答案

JVM 不用引用计数,用可达性分析

以 GC Root 为起点向下遍历引用链,能遍历到的是存活对象,遍历不到的判定为垃圾,可被回收。

优点:解决循环引用无法回收的问题。

17、引用计数为什么不用了?

答案

引用计数靠对象被引用次数,为 0 就回收;

致命缺点:两个对象互相循环引用,计数永远不为 0,永远回收不了,造成内存泄漏,所以 JVM 主流用可达性分析。

18、新生代为什么是 8:1:1 比例?

答案

Eden:S0:S1 = 8:1:1

大部分对象朝生夕死,大多在 Eden 诞生、一次 Minor GC 就死了;

两个 Survivor 来回复制存活对象,节省内存、减少空间浪费

19、什么是对象晋升到老年代?条件有哪些

答案

新生代对象满足任意条件进入老年代:

  1. 对象年龄达到15 岁(默认)
  2. Survivor 放不下存活对象,直接晋升
  3. 大对象直接分配到老年代(避免新生代复制开销)

20、Minor GC 触发条件?

答案

Eden 区满了就触发 Minor GC;

清理新生代,把存活对象复制到 Survivor,年龄达标升入老年代。

21、Full GC 触发条件有哪些?

答案

  1. 老年代空间不足
  2. 方法区 / 元空间不足
  3. 显式调用 System.gc()
  4. Minor GC 后晋升老年代放不下
  5. CMS 并发失败、空间溢出

Full GC 开销极大,项目要尽量避免。

22、JVM 栈溢出和堆溢出区别

答案

  • 栈溢出 StackOverflowError:递归太深、方法嵌套过多,虚拟机栈帧过多
  • 堆溢出 OOM:创建对象太多、大集合不释放、静态常驻对象,堆内存占满

一、基础概念

  1. JVM 作用一次编译到处运行,负责类加载、内存管理、垃圾回收、字节码执行。
  2. JVM 运行时数据区 5 块程序计数器、虚拟机栈、本地方法栈、堆、方法区。
  3. 程序计数器记录字节码执行行号,线程私有,唯一不会 OOM
  4. 虚拟机栈方法执行栈帧,线程私有,递归太深会栈溢出
  5. 存放所有对象和数组,线程共享,GC 主要回收区域
  6. 方法区存类元数据、静态变量、常量,JDK8 由永久代改成元空间。

二、类加载

  1. 类加载过程加载、验证、准备、解析、初始化、使用、卸载。
  2. 双亲委派子加载器先委托父加载器,父加载不了自己再加载;安全防篡改、避免重复加载
  3. 能不能破坏双亲委派能,自定义类加载器不委托父加载器即可,Tomcat 就是典型。

三、GC 垃圾回收

  1. GC 怎么判断垃圾采用可达性分析,从 GC Root 遍历,找不到引用链就是垃圾。
  2. GC Root 有哪些栈引用、JNI 引用、静态变量、常量、活跃线程、锁对象。
  3. 为什么不用引用计数解决不了循环引用,没法回收。
  4. 四种引用强引用永不回收;软引用内存不足回收;弱引用下次 GC 必回收;虚引用只做回收通知。
  5. 新生代比例 8:1:1对象朝生夕死,两块 Survivor 来回复制,节省内存减少浪费。
  6. 对象什么时候进老年代年龄到 15、Survivor 放不下、大对象直接进老年代。
  7. Minor GCEden 满触发,只回收新生代,速度快。
  8. Full GC 触发时机老年代满、元空间不足、Minor GC 晋升失败、手动调用 System.gc ()。
  9. System.gc()只是建议 JVM 回收,不会立即执行,也不保证一定回收。
  10. STW 是什么GC 暂停所有业务线程,只跑 GC 线程,停顿越低体验越好。

四、GC 算法与收集器

  1. 三种垃圾回收算法标记清除有碎片;标记复制无碎片适合新生代;标记整理无碎片适合老年代。
  2. CMS 流程初始标记、并发标记、重新标记、并发清除;低停顿、有碎片、有浮动垃圾
  3. G1 特点把堆分成多个 Region,优先回收垃圾多的区域,可控制最大停顿时间。

五、内存与优化

  1. 栈溢出 vs 堆溢出栈溢出:递归过深栈帧过多;堆溢出:对象 / 集合太多占满堆内存。
  2. 元空间和永久代区别永久代在堆内存易 OOM;元空间用本地直接内存,自动扩容上限更大。
  3. 逃逸分析优化能做栈上分配、标量替换、同步消除,减少堆对象减轻 GC 压力。
  4. 对象内存布局对象头、实例数据、对齐填充。
  5. 内存分配方式内存规整用指针碰撞;有碎片用空闲列表。

六、安卓面试常问收尾

  1. JVM 调优核心思路控制堆大小、优化新生代比例、减少大对象、避免频繁 Full GC、选择合适垃圾收集器。