在 JDK 21 与 27 上真实验证 Shenandoah 的屏障:各模式下 SATB / IU / LoadRef / CAS / Clone / Card 屏障开关的最终值,不能单独关闭;引用读与存储的耗时在各配置下几乎一样;分配密集型负载下暂停都在 0.4 ms 内。
在 JDK 21 与 27 上真实验证字符串去重:30 万个内容重复的 String 使 G1 已用堆从 29.1 MB 降到 10.8 MB;五种收集器都支持;String 对象身份不变,只共享 byte[];年龄阈值为 1 时去重较早发生,3 与 10 在本实验里没有触发。
真实运行验证类卸载:一个自定义加载器在没有强引用时被回收并卸载类;静态字段、ThreadLocal、线程、shutdown hook、线程池、MBean 八种泄漏都使加载器存活;-XX:-ClassUnloading 关闭卸载;五种收集器都支持;Metaspace 16 MB 下 2556 个未卸载的加载器就 OutOfMemoryError。
真实验证堆转储与 OQL:jcmd、jmap、HotSpotDiagnosticMXBean 的转储大小(含不可达 40.76 MB、只含存活 34.07 MB、gzip 2.41 MB);JDK 17/21 里没有 jhat;用 Eclipse MAT 1.16.1 批处理跑 OQL 查对象、字段与保留大小;MAT 默认丢弃不可达对象。
用 JOL 0.17 看 JDK 21 对象布局:12 字节头、字段重排与填充、数组头 16 字节、选项对布局的影响、集合的每元素字节数;并用 500 万个实例实测堆占用核对;JDK 27 默认使用 8 字节紧凑对象头,Q{int x;int y} 实测少 8 字节。
用 -XX:+PrintInterpreter 看 HotSpot 模板解释器:81K 机器码、275 个 codelet、每个字节码一个;用 llvm-mc 反汇编 iadd(8 个指令字,以 br 间接跳转分发);-Xint 每字节码约 0.44 ns,比编译慢约 14 倍;PrintCompilation 找到调用 200 次、回边 60000 次的阈值边界及 128/1024 的检查点。
真实验证常量池解析:没走到的分支里的类不会加载;验证器为了检查赋值兼容会提前加载 LazySub 与 Base 但不加载接口实现;解析失败每次抛新的 NoClassDefFoundError;76 个常量池条目的构成;字符串字面量驻留;每个 lambda 调用点一个类。
在 JDK 21(预览)与 JDK 27 上真实验证 FFM API:直接调 libc 的 getpid / strlen / qsort(带 Java 上行调用),用自己 clang 编出的动态库验证 C 结构体与 MemoryLayout 的大小与偏移一致,各种内存错误的异常,以及两个版本之间 API 与异常的差异。
在 JDK 27 上真实验证 Arena 与 MemorySegment:四种 Arena 的关闭规则、分配时的清零与对齐、3 GB 段的 RSS 与 2 GB 限制、批量操作与堆段的限制、内存映射文件的读写。
在 JDK 27 上用自己编的 C 库验证 Linker:结构体按值传入与返回(16/8/32 字节)、变参函数必须声明 firstVariadicArg(否则输出乱码)、捕获 errno、critical 调用省约 1.2 ns、上行调用的类型检查、上行调用里抛异常会让 JVM 直接退出。
在 JDK 27 上用自己 clang 编的 JNI 库与 Panama downcall 对比:add 调用 JNI 3.73 ns,Panama 局部句柄 6.69 ns、static final 句柄 4.51 ns、加 critical 3.59 ns;小数组 Panama 比 JNI 复制快一个量级;大数组各种传法都落在 C 循环本身的时间。
用 JMH 1.37 真实演示基准陷阱:丢弃结果的 sqrt 与空方法一样 0.3 ns、常量输入被折叠、1000 次循环不是单次的 1000 倍、不逃逸的分配被消除;手写 nanoTime 计时在不同批次里从 10500 ns 降到 8.89 ns,与 JMH 的 4.95 ns 也不同。