diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md
index 6f854b24..fa76f063 100644
--- a/notes/Java 虚拟机.md
+++ b/notes/Java 虚拟机.md
@@ -38,6 +38,10 @@
* [A2 逃逸分析](#a2-逃逸分析)
* [A3 不同收集器的停顿](#a3-不同收集器的停顿)
* [A4 堆大小对 G1 的影响](#a4-堆大小对-g1-的影响)
+ * [A5 ThreadLocalMap 的弱引用](#a5-threadlocalmap-的弱引用)
+ * [A6 默认的收集器与 G1 的 Region 大小](#a6-默认的收集器与-g1-的-region-大小)
+ * [A7 打破双亲委派](#a7-打破双亲委派)
+ * [A8 元空间溢出](#a8-元空间溢出)
* [参考资料](#参考资料)
@@ -132,7 +136,7 @@ void pay(long orderId) {
}
```
-方法返回后,栈帧中的 amount 和 order 立即消失;Order 对象如果没有被其它地方引用,会在之后的 GC 中被回收。操作系统中进程地址空间的栈和堆也是类似的划分:栈由编译器生成的代码自动分配和释放,堆由程序调用 malloc、free 管理。
+方法返回后,栈帧中的 amount 和 order 立即消失;Order 对象如果没有被其它地方引用,会在之后的 GC 中被回收。操作系统中进程地址空间的栈和堆也是类似的划分:栈由编译器生成的代码自动分配和释放,函数返回时局部变量就被释放,不会泄漏;堆由程序调用 malloc、free 管理,申请后忘记 free 就是内存泄漏。
需要注意的是,对象不一定都在堆中创建。JIT 编译时会做逃逸分析,如果一个对象不会离开创建它的方法(没有被返回、没有赋给字段、没有传给其它线程),HotSpot 会把它拆成几个局部变量,这称为标量替换,堆中不再创建这个对象。例如在循环中创建 2 亿个只在方法内部使用的 Point 对象,默认没有发生 GC,耗时约 130 ms;加上 -XX:-DoEscapeAnalysis 关闭逃逸分析后,发生了 13 次 GC,耗时约 335 ms(JDK 21 实测,见[附录 A2](#a2-逃逸分析))。
@@ -172,7 +176,7 @@ Instance size: 32 bytes
- 实例数据:字段不按声明的顺序排列,虚拟机会重排以减少空隙,例如 userId 被放进了对象头后面 4 字节的空位;
- 对齐填充:对象的大小必须是 8 字节的整数倍,new Object() 只有 12 字节的对象头,要补齐到 16 字节。
-关闭压缩指针(-XX:-UseCompressedOops -XX:-UseCompressedClassPointers)后,类型指针和 note 引用都变成 8 字节,Order 从 32 字节变成 40 字节。压缩指针用 32 位存放对象地址,因为对象按 8 字节对齐,地址的低 3 位总是 0,可以省略,所以最多能表示 232 × 8 = 32 GB 的堆。堆超过 32 GB 时压缩指针自动关闭,所有引用都变大,这是堆的大小通常不超过 32 GB 的原因。
+关闭压缩指针(-XX:-UseCompressedOops -XX:-UseCompressedClassPointers)后,类型指针和 note 引用都变成 8 字节,Order 从 32 字节变成 40 字节。压缩指针用 32 位存放对象地址,因为对象按 8 字节对齐,地址的低 3 位总是 0,可以省略,所以最多能表示 232 × 8 = 32 GB 的堆。最大堆达到 32 GB 时,对象引用的压缩自动关闭,所有引用都变成 8 字节(JDK 21 中 -Xmx31g 时 UseCompressedOops 为 true,-Xmx32g 时为 false,类型指针的压缩不受影响),这是堆的大小通常小于 32 GB 的原因。
## 二、垃圾收集
@@ -209,12 +213,15 @@ public class Test {
以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。
-Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容:
+Java 虚拟机使用该算法来判断对象是否可被回收。
-- 虚拟机栈中局部变量表中引用的对象;
+GC Roots 是堆外指向堆中对象的引用,程序不经过其它对象就能直接访问到它们,GC 无法判断这些引用以后还会不会被使用,只能认为它们引用的对象都存活。GC Roots 一般包含以下内容:
+
+- 虚拟机栈中局部变量表中引用的对象,即正在执行的方法的参数和局部变量;
- 本地方法栈中 JNI 中引用的对象;
-- 方法区中类静态属性引用的对象;
-- 方法区中的常量引用的对象。
+- 方法区中类静态属性引用的对象,类没有被卸载时,程序随时可以通过类名访问它们;
+- 方法区中的常量引用的对象,例如字符串常量池中的字符串;
+- 被 synchronized 持有的锁对象、已经启动且没有结束的线程,以及虚拟机内部的引用,例如系统类加载器、常驻的异常对象。
@@ -269,7 +276,7 @@ obj = null; // 使对象只被软引用关联
#### 3. 弱引用
-被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾收集发生之前。
+只被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾收集发生之前。
使用 WeakReference 类来创建弱引用。
@@ -279,6 +286,12 @@ WeakReference