用 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 也不同。
在 JDK 21 上用 JMH 1.37 比较 Blackhole 的三种模式:compiler 下 consume 与 return 都是 0.590 ns/op,full 是 1.106、FULL_DONTINLINE 是 1.929;丢弃结果的计算在所有模式下被删除;consumeCPU 的耗时(1 token 2.291 ns 到 1000 tokens 1705 ns)。
用 JMH 1.37 真实验证为什么要 fork:共用同一个方法的单态基准在多态基准之后(-f 0)慢 4.6 倍(2042.844 对 447.592 ns/op),-f 1 则没有;预热在 1 秒迭代里几乎看不出、在 10 毫秒迭代里第 1 次慢 40%;5 个 fork 之间的差异在 2% 以内。
用 jcstress 0.16 在本机(Apple Silicon)真实观察:Store Buffering 普通字段下 (0,0) 出现 24,354,555 次、volatile 下 0 次;Message Passing 在普通字段下观察到 1,723 次重排序、release/acquire 下 0 次;c++ 丢失更新 28,385,172 次、AtomicInteger 0 次;long 没有撕裂。
在 Apple Silicon 上真实测量缓存的影响:sysctl 报告缓存行 128 字节,步长遍历在步长 32(128 字节)才进入每次访问约 3.5 ns 的平台;二维数组按列遍历比按行慢 4.7 倍;随机依赖读延迟在 128 KB 与 256 KB 之间、8 MB 与 32 MB 之间出现台阶,与 L1 / L2 容量一致。