本期检索说明
先读取 content/collections/deep-radar 截至 2026-09-14 的全部原文链接,并按 URL、标题与问题语义去重。最近 24 小时内,OpenJDK 的 live heap dump 修复给出了真实转储体积、对象级统计与 HotSpot 补丁;Kotlin 增量编译 classpath snapshot 优化则提供六组 JMH 基线、两轮独立改动、源码路径以及三项失败实验。两篇都同时满足“实测数据、源码证据、原理剖析”三项硬指标。
Rust 与 Tokio 近期已经连续覆盖,本期没有用边际较小的编译器优化继续占位;Zig 与 Linux 的新材料也没有形成同等证据闭环,因此不凑数。两项候选在截稿时都仍是开放 PR,本文分析的是可复核的实验与补丁,不把它们写成已发布能力。
| 主题 | 原始发布日期 | 一手来源与交叉验证 | 截稿状态 | 推荐强度 |
|---|---|---|---|---|
| HotSpot live heap dump 跳过 filler object | 2026-09-14 21:42(中原标准时间) | OpenJDK PR #32860 / JDK-8392345 / JOL 对象统计 / HotSpot 源码 | Open,尚未评审、未合入 | 必读 |
| Kotlin IC classpath snapshot 缓存与 FarmHash | 2026-09-14 18:27(中原标准时间) | JetBrains/kotlin PR #8137 / KT-89342 / 六组 JMH / 15 文件补丁 | Open,等待评审、未合入 | 必读 |
01 · HotSpot:live heap dump 不该保存已经不可达的填充对象
原文与日期: Oliver Gillespie,2026-09-14 21:42(中原标准时间);OpenJDK PR #32860、JDK-8392345 与 Thomas Schatzl 对 HotSpot filler object 的机制说明。截稿时 PR 修改 1 个文件、增加 10 行、删除 3 行,尚未完成所需评审。
为什么值得读: “live” heap dump 通常被理解为只保留强制 GC 后仍然可达的对象,但 HotSpot 为维护堆可遍历性而写入的 filler object 仍会进入 HPROF。分析器把它们显示为巨大的 int[],既放大转储文件,又可能让排障者误判为业务数组泄漏。这个补丁把用户语义、GC 内部表示和诊断输出三层边界连接了起来。
核心技术结论: HotSpot 有时需要用可识别对象填补堆中的空洞,使对象迭代仍能从一个合法对象边界走到下一个边界。填充物可以表现为普通对象或数组;在堆转储工具看来,它们与 Java 层对象一样拥有 klass、长度与占用空间。-dump:live 路径会先执行 full GC,但旧实现随后仍遍历并写出这些已经不代表可达用户数据的填充物。
补丁让 HeapObjectDumper 接收 _skip_filler_objects,并在对象写入入口先调用 GC 通用判定:
void do_object(oop o) override {
if (_skip_filler_objects && CollectedHeap::is_filler_object(o)) {
return;
}
// 原有 HPROF 对象写出逻辑
}开关只来自 _gc_before_heap_dump:请求 live dump 时跳过 filler,普通全堆 dump 仍保留原行为。这一点很重要,因为普通 dump 的目标是忠实呈现当时的物理堆,而 live dump 的契约更接近“GC 后的可达对象集合”。实现复用了 CollectedHeap::is_filler_object,没有把某种数组 klass 或特定 GC 的内部布局硬编码进诊断服务。
最有价值的实验、源码与 benchmark 细节: 修复前同一工作负载的 live heap dump 为 2.6GB,修复后为 1.5GB,文件缩小约 42.3%。JOL 对旧转储的统计发现 5 个 int[59217720],每个占 236,870,896 bytes,合计 1,184,354,480 bytes;这个量级与约 1.1GB 的转储差额互相印证。它不是笼统的“压缩更好”,而是把空间差精确追到五个 GC 填充数组。
源码改动集中在 src/hotspot/share/services/heapDumper.cpp:新增 gc/shared/collectedHeap.inline.hpp,在 VM_HeapDumper::work 中把 _gc_before_heap_dump 传给对象 dumper,再在 do_object 的最前面过滤。PR 当前有一项 GitHub Actions 失败被作者标记为与补丁无关;由于它尚未经过完整评审,本期只确认公开样本和补丁逻辑,不推断所有 GC、分段 HPROF 或并发 dump 路径都已验证。
可迁移的排障方法是:看到异常巨型基础类型数组时,不要只按 Java 分配栈推断业务泄漏;先核对数组长度、对象数量、总字节与 dump 文件差额,再判断它是否满足运行时内部填充物特征。诊断工具也应明确选择“物理堆快照”还是“用户可达对象视图”,不要把两种语义藏在同一个遍历器里。
适合的工程师背景: 使用 MAT、JOL、jcmd 或 HeapDumpOnOutOfMemoryError 排查大堆 JVM,并希望理解 GC 堆迭代与 HPROF 边界的性能工程师。
预计阅读收益: 约 35–50 分钟;能识别 heap dump 中伪装成巨大 int[] 的 GC 内部填充物,理解 live 与 non-live dump 的语义差异,并掌握用对象统计反推转储膨胀来源的方法。
02 · Kotlin:复用 ClassReader 与 protobuf,再把 MD5 换成 64 位指纹
原文与日期: Sebastian Sellmair,2026-09-14 18:27(中原标准时间);JetBrains/kotlin PR #8137 与 KT-89342。截稿时 PR 修改 15 个文件、增加 188 行、删除 34 行,已请求评审但尚未合入。
为什么值得读: 这是一份少见的“成功改动与失败实验同样完整”的构建性能记录。作者从 Gradle build scan 中确认 Kotlin 增量编译大量时间消耗在 classpath snapshotting,然后用 JMH 分离大、小 classpath、jar/目录和 class/member 两种粒度。优化没有停在缓存一切,而是逐层验证解码复用、哈希算法与对象布局的真实成本。
核心技术结论: 同一个 class 文件在 snapshot 管线中会被多个阶段读取。旧路径可能重复构造 ASM ClassReader,Kotlin class 还会再次解码 header 中的 protobuf。补丁在 ClassFileWithContents 内惰性共享 classReader 与 classProto,并让 BasicClassInfo.compute、KotlinClassInfo.createFrom、ExtraClassInfoGenerator、SingleClassSnapshotter 和 InlinedClassSnapshotter 显式接收这些已解析结果。第一轮改动因此减少重复扫描与对象创建,六组 benchmark 改善约 7%–18%。
第二轮把 snapshot 内部的字节指纹从 MD5 改为 Guava farmHashFingerprint64:
internal fun ByteArray.hashToLong(): Long =
Hashing.farmHashFingerprint64().hashBytes(this).asLong()旧实现先计算 128 位 MD5,再截取 Long;这里的哈希用于增量构建变更检测而非安全完整性验证,非加密 64 位指纹能显著减少 CPU 成本。不过 64 位碰撞空间与快照兼容性仍是评审必须确认的工程权衡,不能仅凭速度把同一选择搬到制品校验、签名或不受信输入上。
最有价值的实验、源码与 benchmark 细节: JMH 使用 10 次预热、30 次测量、每次 1 秒,表中 Cnt=30。Gradle API 大 classpath 的 class-level 从 1061.723 ± 24.406 ms/op 降至复用解析结果后的 924.898 ± 12.664,再降至 FarmHash 后的 800.664 ± 17.579;member-level 为 1234.256 ± 6.517 → 1141.315 ± 10.781 → 885.063 ± 10.130 ms/op。
小 classpath 同样没有被大样本掩盖。Kotlin stdlib 目录快照 class-level 为 71.565 → 64.082 → 52.237 ms/op,member-level 为 72.159 → 63.991 → 51.158;jar 快照 class-level 为 49.601 → 41.459 → 30.024,member-level 为 50.991 → 41.787 → 29.691。最终相对基线改善约 28%、29%,jar 路径最高 41%。误差没有覆盖这些主要差异。
更值得保留的是三个负向实验。按 jar entry 精确预分配 buffer 对 stdlib 几乎无影响,却让 Gradle API jar 回退约 10%;直接遍历 ASM ClassNode 做指纹显著变慢,作者推测是哈希了更多原始数据且对象内存分散;复用 ClassNode 同样回退,说明“少一次序列化”并不自动胜过连续、去重后的 byte array。它们共同表明,缓存对象图可能把分配成本换成更差的 locality,必须由端到端 benchmark 决定。
作者披露 AI 用于提出实验方向和解释假设,但最终补丁、取舍和所有 benchmark 均由作者编写与确认;其它建议没有在测量中存活。工程上可迁移的结论是:先用共享的原始字节派生一次解析结果,让消费者复用;哈希算法按威胁模型选择;优化 classpath 工具链时必须同时测大 jar、小 jar、目录与不同快照粒度。
适合的工程师背景: 维护 Kotlin Gradle Plugin、增量编译器、字节码索引或大型多模块构建,并熟悉 ASM、protobuf 与 JMH 的工程师。
预计阅读收益: 约 60–90 分钟;能获得一套从 build scan 到微基准、从重复解析到数据布局的完整优化路径,也能看到三种看似合理的缓存/预分配方案为何被实测否决。
中国大陆 ToC 硬件价格观察
数据口径
窗口为 2026-08-17 至 2026-09-15,单位人民币。六类硬件继续固定同一 SKU。9 月 15 日实际打开 CPU 固定样本页,页面显示京东最低 ¥2,619,因此新增当日采集点;GPU、主板、DRAM、HDD 与 SSD 页面本次请求异常,无法取得同日、同型号、同渠道且可复核的新报价,不新增点,也不把旧值复制成今日行情。折线只连接 JSON 中真实采样日,空白尾段表示证据缺失而不是横盘。
| 类别 | 固定样本与最后可复核价 | 30 日窗口内真实走势 | 建议 |
|---|---|---|---|
| CPU | Ryzen 7 9800X3D,9/15 ¥2,619 | 8/21 ¥2,609 → 9/9 ¥2,449 → 9/14 ¥2,639 → 9/15 ¥2,619;仍高于窗口低点 6.94% | 观望;单日回落 ¥20 尚不足以确认重新下行,¥2,449–2,599 可作为关注区间 |
| GPU | MSI RTX 5070 VENTUS 3X 12G,最后可核验 9/13 ¥4,464.34 | 8/29–9/13 六个真实采集点均为 ¥4,464.34 | 继续观望;固定样本未出现下行证据,付款前重核大陆保修与渠道 |
| 主板 | ASUS TUF B850M-PLUS WIFI7,最后可核验 9/8 ¥1,299 | 8/31 ¥1,449 → 9/2–9/8 ¥1,299,随后缺少新点 | 刚需可关注 ¥1,299;付款前重核 WIFI7 版本与售后 |
| DRAM | Kingston FURY Beast DDR5-6000 32GB,最后可核验 9/9 ¥3,900 | 9/2 ¥3,900,9/5 ¥3,860,随后回到 ¥3,900 | 观望;报价新鲜度不足,不能按缺失日期推断回落 |
| HDD | IronWolf Pro 8TB ST8000NE001,最后可核验 9/10 ¥1,849 | 9/1–9/10 五个真实点均为 ¥1,849 | 按容量刚需购买;优先比较五年保修与数据恢复服务 |
| SSD | ZHITAI Ti600 2TB,最后可核验 9/10 页面最低 ¥749 | 9/2 ¥1,919 → 9/5–9/10 ¥749;同页渠道价差异常大 | 谨慎核验后入手;先确认容量、颗粒、保修与商家 |
价格来源: 9800X3D、MSI RTX 5070 VENTUS 3X、ASUS TUF B850M-PLUS WIFI7、Kingston FURY DDR5-6000 32GB、IronWolf Pro 8TB、ZHITAI Ti600 2TB。
判断依据
CPU 从 9 月 9 日低点反弹 ¥190 后,今天只回落 ¥20,仍不足以证明低点已经回归;建议来自同一 SKU 的观察区间,不外推全市场均价。其余五类没有当日可核验页面,因此建议仅依赖最后真实采样和渠道风险,不对不可见库存、促销或原厂产能作推测。SSD 的最低价与前序记录差异过大,继续作为需要核对规格和商家的价格事件,而不是全市场跌幅。
其它你可能关心的
- OpenJDK:primitive instanceof 常量折叠与 bytecode 测试
- OpenJDK Valhalla:C2 extended frame 下的栈溢出修复
- Tokio:面向 WASM event loop 的 current-thread runtime 设计
本期结论
两篇材料都说明“重复的内部表示”会悄悄变成诊断或构建成本:HotSpot 把维护堆结构的填充对象暴露为用户可见数组,Kotlin 则让同一 class 字节在多个 snapshot 阶段重复解析和使用过重哈希。可靠的优化不是简单删对象或加缓存,而是先明确输出语义、数据生命周期和威胁模型,再用对象级统计或多维 JMH 验证。尤其值得借鉴的是负向数据:它能阻止团队把局部少分配误写成端到端更快。