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

Compiler Queue:哪些方法正在等待 JIT 编译

用 jcmd Compiler.queue 在 JDK 21 上观察 JIT 编译队列:3000 个小方法的洪水下 C2 队列最长 9、同时编译 3 个(C1 队列始终为空);CICompilerCount 从 2 到 8 总用时 1861 → 1666 ms;-Xbatch 预热慢近 2 倍(1739 vs 898 ms);没有观察到热点方法被队列拖慢。

Java JIT 编译队列 jcmd CICompilerCount Xbatch 真实验证 动画演示
Java 内部原理 2026-10-05

对象分配预取:内存带宽如何影响分配速度

在 macOS arm64 的 JDK 21 上量对象分配速度:默认 AllocatePrefetchStyle = 1、Distance = 384;各 style 之间的差异与同一配置三次运行的波动同量级;Distance = 512 对 64 字节差 23%、对 1 KiB 好 40%;小对象约 2 ns 与大小无关;多线程总带宽不线性增长且包含 GC。

Java 内存 对象分配 TLAB AllocatePrefetch 带宽 真实验证 动画演示
Java 内部原理 2026-10-05

Shutdown Hook:进程退出时如何做最后清理

在 JDK 21 / macOS 上实测 Shutdown Hook:正常返回、System.exit、未捕获异常、SIGINT / SIGTERM / SIGHUP 会运行(退出码 130 / 143 / 129),Runtime.halt 与 kill -9 不会;多个 hook 并发且顺序不定;SIGTERM 后 1 秒 kill -9 只完成 3 / 10 步清理;hook 里 System.exit 不退出、addShutdownHook 抛异常。

Java ShutdownHook 进程退出 信号 SIGTERM 清理 真实验证 动画演示
Java 内部原理 2026-10-05

JVM 进程退出钩子与退出状态

在 JDK 21 上实测退出码:System.exit(256) 是 0、300 是 44、-1 是 255;子线程的未捕获异常不改变退出码;两个线程各 exit 的结果 18 / 12 随机;ExitOnOutOfMemoryError 的退出码是 3,默认 OOM 在子线程发生时进程继续并以 0 退出;java 启动器各类错误都是 1。

Java 进程退出 退出码 System.exit OutOfMemoryError 启动器 真实验证 动画演示
Java 内部原理 2026-10-05

Unsafe 的历史与替代方案

在 JDK 17 / 21 / 27 上实测 sun.misc.Unsafe 与替代方案:JDK 27 默认打印 terminally deprecated 警告,deny 时抛异常;objectFieldOffset 对 record 与隐藏类抛 UnsupportedOperationException;MemorySegment / VarHandle / ByteBuffer 与 Unsafe 的读写都约 0.5 ns;VarHandle 与 Unsafe 的 CAS 成本相同;Unsafe 不 free 的 412 MiB 在 gc 后仍在,Arena 有边界与生命周期检查。

Java Unsafe VarHandle MemorySegment Foreign Memory 堆外内存 真实验证 动画演示
Java 内部原理 2026-10-05

Files.mismatch:高效比较两个文件的第一个差异

在 JDK 21 上实测 Files.mismatch:返回第一个差异的下标、前缀返回较短长度、相同返回 -1;耗时与需要比较的字节数成正比(差异在开头 0.1 ms、在末尾 40.9 ms,128 MiB);比 readAllBytes + Arrays.equals 少分配 213 MiB、比 SHA-256 快 3.6 倍;大小不同、前缀相同时仍然要读完较短的文件。

Java NIO Files.mismatch 文件比较 性能 真实验证 动画演示
Java 内部原理 2026-10-05

FileChannel.lock:跨进程文件锁的边界

在 JDK 21 / macOS 上用两个 JVM 实测 FileChannel.lock:tryLock 返回 null、lock() 阻塞 1934 ms、共享与区域锁的规则;同一 JVM 内重叠抛 OverlappingFileLockException;锁只是建议性的(B 不申请锁照样写入);kill -9 后立即释放,但关闭同进程内同文件的另一个通道会丢锁(isValid 仍为 true);锁文件被删除后两个进程都拿到锁。

Java NIO FileChannel 文件锁 FileLock 跨进程 真实验证 动画演示
Java 内部原理 2026-10-05

AsynchronousSocketChannel:异步网络 I/O 的完成回调

在 JDK 21 上实测 AsynchronousSocketChannel:完成回调运行在线程组的线程上;数据已就绪时回调会直接嵌套调用 16 层(sun.nio.ch.maxCompletionHandlersOnStack 默认值,设 0 后栈深度恒为 11);每个通道只能有一个未完成的 read 与 write;write 只写出 319 KiB 就完成;读超时之后通道不可再读;未关闭的 accept 通道会让线程组无法终止。

Java NIO.2 AsynchronousSocketChannel 异步IO CompletionHandler 真实验证 动画演示
Java 内部原理 2026-10-05

ProcessHandle:观察和管理子进程

在 JDK 21 / macOS 上实测 ProcessHandle:info() 能读到命令与 CPU 时间但只有 454 / 742 个进程有 command;descendants() 比 children() 多孙进程,只杀父进程后 4 个后代全部存活并改挂到 pid 1;onExit() 对非子进程也有效,延迟约 3–13 ms;destroy() 返回 true 但忽略 SIGTERM 的进程仍然活着,destroyForcibly 得到退出码 137。

Java 进程 ProcessHandle destroy onExit descendants 真实验证 动画演示
Java 内部原理 2026-10-05

ProcessBuilder:安全地启动外部命令

在 JDK 21 / macOS 上实测 ProcessBuilder:拼接 sh -c 会执行注入的命令而数组参数不会;Runtime.exec(String) 不识别引号;不读输出时子进程写到 70000 字节就阻塞(管道约 64 KiB),stderr 同样;pb.start() 用 Java 进程的 PATH 查找可执行文件而 sh -c 用子进程的 PATH;启动失败的错误码 2 / 13 / 7。

Java 进程 ProcessBuilder 命令注入 管道 环境变量 真实验证 动画演示
Java 内部原理 2026-10-05

System.Logger:模块化环境中的轻量日志接口

在 JDK 21 上实测 System.Logger:默认后端是 java.util.logging(TRACE / DEBUG 不输出,ERROR 显示为 SEVERE);Supplier 在级别禁用时不求值而 expensive() 直接调用求值 1000 次;占位符是 MessageFormat 的 {0} 而不是 %s;禁用时字符串拼接 10.8 ns 而 Supplier 约 2 ns;自定义 LoggerFinder 能收到 JDK 的 Runtime.exit 日志。

Java 日志 System.Logger java.util.logging LoggerFinder 模块 真实验证 动画演示
Java 内部原理 2026-10-05

Console:交互式终端中的安全输入

用伪终端在 JDK 17 / 21 / 27 上实测 Console:管道里 System.console() 为 null,伪终端里非 null(JDK 27 多了 isTerminal());readPassword 不回显并返回可清零的 char[],而 Scanner 与 readLine 会回显;Ctrl-D 让 readPassword 返回 null;LC_ALL=C 时 Console.charset() 是 US-ASCII 而 defaultCharset() 仍是 UTF-8。

Java Console readPassword 终端 密码输入 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号