知其然,知其所以然
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 真实验证 动画演示
Java 内部原理 2026-10-03

ZIP 文件系统:把压缩包当作 Path 来访问

把 zip 当作 Path 访问:创建时磁盘只有 22 字节的空 zip、close 后 743 字节;往 50 MB 的 zip 加一个条目 close 耗时 53 ms 并换成新文件;releaseVersion 让同一路径读到多版本 jar 里不同版本的类;jar: URI 重复打开抛 FileSystemAlreadyExistsException。

Java ZIP文件系统 zipfs NIO.2 Path 真实验证 动画演示
Java 内部原理 2026-10-03

Direct ByteBuffer 池:堆外缓冲如何复用

真实验证 direct buffer:内存不在堆里、要等 GC 的 Cleaner 释放;allocateDirect 比堆分配贵(1 MB:45249 ns 对 21712 ns)而复用几乎免费;Netty 池化 16 KB 每次 52 ns 对 JDK 的 1640 ns;MaxDirectMemorySize=64m 且 DisableExplicitGC 时,丢弃的 buffer 也会在第 65 个 OOM。

Java DirectByteBuffer 堆外内存 Netty 内存池 真实验证 动画演示
Java 内部原理 2026-10-03

TLS 会话复用:减少重复握手成本

本机真实测量 TLS 会话复用:完整握手 TLS 1.3 4.9 ms / TLS 1.2 4.4 ms,复用后 1.4 / 0.5 ms;TLS 1.3 每次发新票据(session id 不同)而 TLS 1.2 的 id 不变;按 主机名:端口 缓存;默认启用票据时服务器的会话超时与缓存大小不起作用,关闭票据后才生效。

Java TLS 会话复用 session ticket JSSE 真实验证 动画演示
Java 内部原理 2026-10-03

ALPN:TLS 握手如何协商应用层协议

真实验证 ALPN:JDK 服务器按自己的协议列表顺序挑选;没有交集抛 no_application_protocol,一方没设置则握手成功且协议为空字符串;selector 返回 null 失败、返回空字符串不使用 ALPN;用真实的 nginx、openssl、curl 与 Java HttpClient 验证 h2 与 http/1.1 的协商和回落。

Java TLS ALPN HTTP/2 nginx 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号