Skip to content

Commit b1f0d37

Browse files
committed
📖dfs
1 parent 18e65ff commit b1f0d37

File tree

2 files changed

+83
-8
lines changed

2 files changed

+83
-8
lines changed

docs/.vuepress/dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit 4f4dae00c8defc265850229aa01e8f1c4aedab22
1+
Subproject commit 73649b860108b81cbcc8f505c077d5d287e3190d

docs/interview/JVM-FAQ.md

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -906,17 +906,92 @@ JVM 在我们创建 Java 对象的时候去分配新内存,并使用 GC 算法
906906

907907
### 对象的死亡过程?
908908

909-
1. 第一次标记
909+
Java 中,对象被垃圾回收器(Garbage Collector, GC)判定为“死亡”并回收内存的过程,涉及 **两次标记** 和潜在的 **自救机会**(通过 `finalize()` 方法)
910910

911-
​ 对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记。
911+
**一、第一次标记:可达性分析**
912912

913-
2. 第二次标记
913+
1. **触发条件**:当 JVM 开始垃圾回收时,首先通过 **可达性分析算法(Reachability Analysis** 判断对象是否存活。
914914

915-
假如对象没有覆盖 finalize 方法,或者 finalize 方法已经被虚拟机调用过,那么不执行 finalize 方法。
916-
如果有必要执行 finalize 方法,那么该对象将会被放置在一个名为 F-Queue 的队列之中,并在稍后由一条由虚拟机自动建立的、低调度优先级的 Finalizer 线程去执行它们的 finalize 方法。
915+
2. **GC Roots 的引用链**
917916

918-
finalize 方法是对象逃脱死亡命运的最后一次机会,稍后收集器将对 F-Queue 中的对象进行第二次小规模的标记,如果对象要在 finalize 中成功拯救自己,只要重新与引用链上的任何一个对象建立关联即可。
919-
如果对象这时候还没有逃脱,那基本上它就真的要被回收了。
917+
- GC Roots 对象包括:
918+
- 虚拟机栈(栈帧中的局部变量表)引用的对象。
919+
- 方法区中静态变量引用的对象。
920+
- 方法区中常量引用的对象(如字符串常量池)。
921+
- JNIJava Native Interface)引用的本地方法栈对象。
922+
923+
- **遍历过程**:从 GC Roots 出发,递归遍历所有引用链。未被遍历到的对象即为不可达对象。
924+
925+
3. **第一次标记结果**
926+
927+
- **存活对象**:与 GC Roots 存在引用链,继续保留。
928+
929+
- **待回收对象**:不可达,被标记为“可回收”,进入第二次标记阶段。
930+
931+
**二、第二次标记:finalize() 方法的自救机会**
932+
933+
1. **筛选条件**
934+
935+
- 若对象未覆盖 `finalize()` 方法,或 `finalize()` 已被调用过,则直接判定为死亡,无需进入队列。
936+
937+
- 若对象覆盖了 `finalize()` 且未被调用过,则将其加入 **F-Queue 队列**,进入自救流程。
938+
939+
2. **F-QueueFinalizer 线程**
940+
941+
- **F-Queue**:一个低优先级的队列,存放待执行 `finalize()` 的对象。
942+
943+
- Finalizer 线程:JVM 创建的守护线程,负责异步执行队列中对象的 `finalize()` 方法。
944+
- **注意**:`finalize()` 的执行不保证完成(如线程优先级低或方法死循环)。
945+
946+
3. **自救机制**
947+
948+
在 `finalize()` 方法中,对象可通过重新与 GC Roots 引用链建立关联来自救:
949+
950+
```java
951+
public class Zombie {
952+
private static Zombie SAVE_HOOK;
953+
954+
@Override
955+
protected void finalize() throws Throwable {
956+
super.finalize();
957+
System.out.println("finalize() 执行,对象自救");
958+
SAVE_HOOK = this; // 重新建立与 GC Roots 的关联
959+
}
960+
961+
public static void main(String[] args) throws Exception {
962+
SAVE_HOOK = new Zombie();
963+
SAVE_HOOK = null; // 断开引用,触发 GC
964+
System.gc();
965+
Thread.sleep(500); // 等待 Finalizer 线程执行 finalize()
966+
if (SAVE_HOOK != null) {
967+
System.out.println("对象存活");
968+
} else {
969+
System.out.println("对象被回收");
970+
}
971+
}
972+
}
973+
974+
975+
----- 输出结果
976+
finalize() 执行,对象自救
977+
对象存活
978+
```
979+
980+
- 关键点:
981+
- 对象通过 `finalize()` 将 `this` 赋值给静态变量 `SAVE_HOOK`,重新建立与 GC Roots 的引用链。
982+
- 自救仅生效一次,第二次 GC 时对象仍会被回收。
983+
984+
**三、对象回收的最终判定**
985+
986+
1. **第二次标记结果**
987+
988+
- **自救成功**:对象重新与引用链关联,移出待回收集合。
989+
990+
- **自救失败**:对象仍不可达,被标记为“死亡”,等待内存回收。
991+
992+
2. **回收内存**
993+
994+
根据垃圾收集算法(如标记-清除、复制、标记-整理等),将死亡对象的内存回收。
920995

921996

922997

0 commit comments

Comments
 (0)