用 -XX:+PrintInlining 在 JDK 21 上验证 JIT 内联:4 / 105 / 157 / 521 字节的方法在 C1 与 C2 两次编译中的内联决定(C2 内联 105 与 157 字节的,拒绝 521 字节的 hot method too big);改 FreqInlineSize 与 -Inline 的耗时;调用点接收者 1 / 2 / 3 / 4 种类型时 0.51 / 0.69 / 3.26 / 3.24 ns。
在 JDK 21 上验证 OSR:只被调用一次的 main 里的长循环,默认每迭代 0.7 ns,关闭 OSR 后停在解释器里 26 ns(与 -Xint 的 23.6 ns 相当);PrintCompilation 里带 % 与 @bci 的 OSR 记录经历 C1 → C2 → 作废 → 再 C1 → 再 C2 两轮;嵌套循环的 OSR 入口在内层循环头。
在 JDK 21 / 17 上验证元空间:300 个加载器 × 150 个类时 capacity 107.61 MB、used 98.10 MB,全部丢弃后 committed 从 106.92 MB 降到 3.82 MB;同样丢弃一半加载器,交错丢弃比丢弃前一半多留 32.79 MB 已提交的空闲 chunk(虚拟空间 committed 78.81 vs 55.50 MB);JDK 21 忽略 MetaspaceReclaimPolicy,JDK 17 的 none 丢弃后 committed 不降。
在 JDK 21 上复现类初始化死锁:A、B 互相引用的静态初始化器,以及 Parent 持有 Child 实例的无显式环的例子;两个线程都是 RUNNABLE,ThreadMXBean 的死锁检测返回 null,线程转储里看 “waiting on the Class initialization monitor”;时间窗口小于 1 ms 时 40/40 死锁,晚 5 ms 启动则 0/40。
在 JDK 21 上用真实的 jstack 命令读线程转储:BLOCKED / WAITING (on object monitor) / WAITING (parking) / TIMED_WAITING 各自的栈写法;jstack -l 的 Locked ownable synchronizers(189 → 249 行);用 cpu= 差值找 100% CPU 的线程;死锁段落(synchronized 与 ReentrantLock 各一对,不加 -l 也检测到两对);虚拟线程 jstack 看不到(0 个),Thread.dump_to_file 能。
在 JDK 21 上用 jcmd 逐个调用:help 列出 49 个命令;VM.set_flag 只能改 13 个 manageable 参数(MinHeapFreeRatio 40 → 55,MaxHeapSize 被拒绝);GC.run 把 used 32 MB 降到 1 MB;NMT 的 baseline / summary.diff 看到 100 MB 直接内存在 Other 里 +102400KB;JFR.start / dump / stop 的一次完整录制。
在 JDK 21 上实测 jmap:-histo 含 300 万个不可达对象(6645923 个 / 387 MB),-histo:live 先 Full GC 后只剩 631197 个;-dump 454.1 MB 对 -dump:live 137.2 MB,应用最大停顿 263 ms 对 43 ms;相对路径相对于 jmap 的工作目录;-heap 在 JDK 21 上不能用;jcmd GC.heap_dump 默认就是 live,与 jmap 默认相反。
在 JDK 21 / macOS 上实测 jhsdb:jstack --pid 给出死锁与带 @bci、Compiled/Interpreted 标注的栈;jhsdb 退出后目标进程仍处于 T(停住)状态,jcmd 无响应,手工 SIGCONT 后心跳最大间隔 12247 ms;jmap --histo 比 jmap -histo 慢约 30 倍;clhsdb 的 universe / threads;core 文件(3349 MB)用 jhsdb --core 读取失败。
在 JDK 21 上验证 java.time.Clock:fixed / offset / tick 的行为与 tick 的时长限制;注入 Clock 的过期测试 3.38 ms 对真实时钟 sleep 的 1254 ms;LocalDate.now() 与 Clock.fixed(…, systemDefault) 随默认时区而变;macOS 上 Instant.now() 是微秒分辨率;tickSeconds / tick(100 ms) 的取整;模拟时钟回拨时 Clock 差值为负。
在 JDK 17/21/27 上验证 ZoneRules:纽约 2026 年夏令时的空洞(0 个有效偏移)与重叠(2 个);一天是 23 / 24 / 25 小时,plusDays 与 plus(Duration.ofHours(24)) 差一小时;Asia/Shanghai 的历史偏移(+08:05:43、1988 年夏令时);New_York 177 条显式转换 + 2 条重复规则;三个 JDK 的 tzdata 都是 2026b;ZoneId.of 对 EST、大小写、空格的处理。
在 JDK 21 上验证 TemporalAdjuster:内置 next / nextOrSame / firstInMonth 等的结果与含不含当天;dayOfWeekInMonth(5 / 0 / -5) 在只有 4 个星期五的月份溢出到相邻月份;自写 NEXT_WORKING_DAY 与手写循环逐天比对 4018 天零不一致;适用于 LocalDate / ZonedDateTime / 泰佛历等,但对 YearMonth、LocalTime、Instant 抛异常;与夏令时空洞的交互。
在 JDK 21 上验证 MessageFormat:六个 Locale 下数字与日期的差别;整数 2026 变成 “2,026”;单引号会吞掉后面的占位符;ChoiceFormat 做复数的区间与边界,以及对俄语 101、102、1001 给出错误形式;参数不够时占位符原样保留;8 线程共用一个实例 16 万次里 80146 次结果错误并抛异常;静态 format 约 1.1 µs,复用实例约 0.32 µs。