知其然,知其所以然
Java 内部原理 2026-10-03

Safepoint:所有线程为什么会短暂停下来

用 -Xlog:safepoint 看 JDK 21 的安全点:一个小程序 25 次安全点,到达安全点通常只要几微秒;去掉计数循环里的安全点检查后 Thread.getAllStackTraces() 从 0 ms 变成约 1100 ms、System.gc() 变成 1066 ms;getThreadInfo 也是全局安全点;空闲 JVM 没有周期性安全点。

Java Safepoint 安全点 JIT GC日志 真实验证 动画演示
Java 内部原理 2026-10-03

TLAB:线程如何快速分配小对象

用 -Xlog:gc+tlab=trace 和分配基准看 TLAB:有 TLAB 时 1/8 线程的小对象分配 194/596 M 对象/秒,-XX:-UseTLAB 只有 13/3;TLAB 随线程数自适应变小;-XX:TLABSize=64k 使吞吐降到 132 M/秒;TLABSize=4m 被 JVM 拒绝。

Java TLAB 对象分配 JVM调优 真实验证 动画演示
Java 内部原理 2026-10-03

逃逸分析:对象什么时候可以不进堆

用每次迭代分配的字节数验证 C2 逃逸分析:不逃逸的 24 字节对象默认 ≈0 字节而关闭 EA 后 24 字节;只在 1/1024 的路径上逃逸就消除不了;大方法、int[100]、List 迭代器、Optional 链没被消除;局部装箱、StringBuilder、锁被消除。

Java 逃逸分析 标量替换 JIT 锁消除 真实验证 动画演示
Java 内部原理 2026-10-03

Compressed Oops:对象引用为什么可以用更少位数

验证压缩指针:-Xmx 到 31900m 默认开启、32g 起关闭,对象对齐 16 时上限 64g;对象大小开启与关闭对比(Node 32/40/48 字节);500 万个节点堆占用 155 MB 对 194 MB;JDK 27 默认紧凑对象头使 new Object() 只有 8 字节、堆占用 117 MB。

Java Compressed Oops 压缩指针 对象头 内存占用 真实验证 动画演示
Java 内部原理 2026-10-03

CDS 与 AppCDS:把类加载工作提前到归档阶段

用 Netty 小程序验证 CDS 与 AppCDS:默认 CDS 让 937/1581 个类来自归档;启动 145 → 122 → 68 ms(-Xshare:off / 默认 / AppCDS 动态归档);应用类在目录里无法创建动态归档;jar 变化后 -Xshare:on 失败,AutoCreateSharedArchive 不会自动重新生成。

Java CDS AppCDS 类加载 启动时间 真实验证 动画演示
Java 内部原理 2026-10-03

分层编译:解释器与 JIT 如何接力

用 PrintCompilation 与基准验证分层编译:hot/mid 先 C1 层级 3 再 C2 层级 4,长循环的 C2 OSR 一次被跳过而回退到层级 1;每次调用耗时从 137 ns 降到约 16 ns;3000 万次调用 -Xint 10742 ms、只用 C1 573 ms、默认 401 ms、只用 C2 760 ms。

Java 分层编译 JIT C1 C2 PrintCompilation 真实验证 动画演示
Java 内部原理 2026-10-03

Deoptimization:优化假设失效后 JVM 如何退回解释执行

用 -Xlog:deoptimization=debug 验证 deopt:调用点实现数 1/2/3/4 种时每次调用 0.69/0.83/3.56/3.17 ns 且不会退回;unstable_if 让首批慢 7 倍;数组越界触发 range_check 与 unstable_if 的 trap。

Java Deoptimization JIT uncommon trap 类型profile 真实验证 动画演示
Java 内部原理 2026-10-03

Code Cache:JIT 生成的机器码放在哪里

用 jcmd Compiler.codecache 与故意缩小的代码缓存看 JIT 代码放在哪里:默认 240 MB 分三段;6 MB 且关闭刷新时 2500 个热方法把它填满,「CodeCache is full. Compiler has been disabled.」之后新的热方法慢到 240.13 ns/次(默认 8.89);刷新开着时同样的负载没有填满。

Java CodeCache 代码缓存 JIT jcmd 真实验证 动画演示
Java 内部原理 2026-10-03

类加载约束:同名类型为什么不能互相替代

用两个自定义类加载器复现类加载约束:同名类在不同加载器里是不同的类(Cannot cast lib.Dep to lib.Dep);同一加载器重复 defineClass 抛 LinkageError;签名里的类型被子加载器重复定义后,调用触发 loader constraint violation,而且 P 之后连自己的 Dep 都加载不了。

Java 类加载器 LinkageError loader constraint ClassCastException 真实验证 动画演示
Java 内部原理 2026-10-03

字节码验证器:类加载前 JVM 如何检查类型安全

用 ASM 手工造七种坏 class 看字节码验证器:每种 VerifyError 的 Reason;defineClass 与 Class.forName(…,false) 不验证,getDeclaredMethods 与 invoke 才触发;验证 3000 个类 41 ms vs 不验证 30 ms;关掉验证再加载坏类 JVM 直接崩溃。

Java 字节码验证 VerifyError ASM 类加载 真实验证 动画演示
Java 内部原理 2026-10-03

Selector.wakeup:如何唤醒阻塞中的事件循环

真实验证 Selector.wakeup:阻塞中的 select 被 wakeup 唤醒中位 30 μs;提前 wakeup 只保留一次、1000 次合并成一次、selectNow 会清除;被 interrupt 不抛异常且中断标志保留(带标志循环 300 ms 空转 392,428 次);select 阻塞时 register 或改 interestOps 不会唤醒它,等到 1895 ms 超时,要靠 wakeup。

Java NIO Selector wakeup 事件循环 真实验证 动画演示
Java 内部原理 2026-10-03

FileChannel.transferTo:零拷贝究竟省掉了什么

把 1 GB 文件发到本机 socket:read/write 8 KB 堆缓冲区 0.73 s、1 MB 直接缓冲区 0.2 s、transferTo 0.17 s、mmap 0.27 s(常驻内存 1120 MB);缓冲区大小的影响远大于是否用 transferTo;文件到文件 transferTo 0.25 s 对 Files.copy 0.58 s。

Java 零拷贝 FileChannel transferTo NIO 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号