我如何借助 JProfiler MCP 让 jclasslib 的性能翻倍
最近,我借助 Codex 和一个性能分析智能体,提升了我的开源 JVM 字节码查看器 jclasslib 的性能。这并不是因为我有某个具体的痛点,而是因为我想试用随 JProfiler 16.1 一起发布的 MCP 服务器。请继续阅读,了解这个智能体如何让类文件读取提速一倍以上,以及为什么性能分析不再需要理由就能开始。
起点
jclasslib 由两个主要部分组成:一个读写字节码的库,以及一个显示并编辑字节码的 Swing UI。该项目启动时,读取类文件的事实行业标准 ASM 还不存在。由于查看器与类文件读取器产出的表示紧密绑定,改用其他库从来都没有意义。
ASM 针对资源占用做了深度优化,采用访问者模式,而不是像 jclasslib 那样把完整结构读入内存。这一点从来不是问题,因为实际显示的那少数几个类文件读取起来耗时极少。然而,jclasslib 7.0 中新增的 Find usages(查找用法)功能会扫描整个类路径。类文件读取速度忽然变得可感知了,不过仍在可接受范围内,所以我没有深究。


优化
JProfiler MCP 一发布,我就让 Codex 尝试提升类文件读取的速度,它很快便产出了一系列提交。最能清楚说明这个智能体发现了什么的,是这一个提交。该提交移除了一次代价高昂的重复 Map 创建。这我在代码里绝不会注意到,即使查看性能分析数据,它也会被归入“程序本来就需要这么做”这一类。而 Codex 把这两处 Map 创建追溯到了各自的源头,并将它们合并成了一处。
仅这一个提交,就让经过性能分析的单元测试从 662 毫秒降到了 410 毫秒,提速 38%。同一次运行中的其他提交补齐了剩下的部分:
- cb992aac:从读取路径中移除了重复的 Map 插入
- c7c0155d:对纯 ASCII 的 UTF-8 条目跳过 CharArray 分配
- 4798108d:常量池字符串的延迟解码
- a1ae6e7d:加快了 Find usages 所需的类路径扫描
另外还有几个更小的改动。累计来看,读取 JDK 25 JRE 中的全部类文件(25000 多个类)的耗时从 851 毫秒降到了 347 毫秒,类文件读取速度几乎提升至原来的 2.5 倍。这就是标题中“翻倍”的由来。
源码检视与性能分析数据
我很好奇:如果没有 JProfiler MCP 服务器,这个编码智能体是否也能发现这些问题,于是我问了它。下面的回答我原样保留、未作编辑,因为这个智能体的推理过程本身与其得出的结论同样重要。
问:JProfiler MCP 有多重要?与只检视源码相比,结果好了多少?
答:它对这项任务很重要。没有性能分析器的话,我很可能会先花时间去优化错误的对象。
- 它提供了一条从“在性能分析下运行这个确切的测试”到可用热点数据的快捷路径。
prepare_profiling返回可直接传入的 JVM 参数,这一点工作得很好。- 整个工作流简单且可组合:
prepare_profiling→ 运行 Gradle 测试 →check_status→get_performance_hotspots→expand_performance_hotspot。- 热点展开的粒度足以区分解析器的开销与测试脚手架的开销。
- 它直接揭示出了真正的收益点:
ClassFile.setConstantPool中预先执行的常量池反向索引构建,及其下游String.hashCode/HashMap.put的开销。仅凭源码层面猜测,这并不是最显而易见的目标。- 它避免了一次很可能发生的错误优化。纯源码检视会让
ConstantUtf8Info.readUTF看起来像是主要目标。性能分析显示它确实很热,但并不是第一步该做的最佳修复。比源码检视好多少?
- 从定性角度看:好得多。
- 仅凭源码检视,多半会先尝试优化修改版 UTF-8 的解码,也许会去看遍历 JRT 的开销,或者尝试在结构读取中做些泛泛的微优化。
- 性能分析表明,价值最高的修复在别处:移除每次读取类文件时预先进行的反向索引构建。
- 实测改进为从约 662 毫秒降到 410 毫秒,大约提速 38%。
- 我认为,仅凭源码检视,我不会把那个具体的修复当作第一步。
总体重要性
- 对这项任务而言:很高。
- 它把操作顺序从“猜测、修改、跑基准”变成了“测量、修复最大的真实开销、验证”。
- 最终结果既更快也更有说服力,因为它依据的是观测到的热点,而不是直觉。
反超 ASM
过程中发生了一件出乎我意料的事:在对查看器最重要的那些模式上,jclasslib 反超了 ASM。仓库中的 AsmPerformanceComparisonTest 会用这两个库扫描 JRE 中的每一个类,并报告 jclasslib 耗时与 ASM 耗时之比。比值为 1.0 表示持平,低于 1.0 表示 jclasslib 更快。在 JDK 17 上优化前的基线中,只读模式下的 jclasslib 比 ASM 慢约一倍(比值 1.95)。在当前的 HEAD 上,对同样这 26,653 个类运行相同的基准测试,现在报告:
- 读取完整类结构:0.77(jclasslib 347 毫秒,ASM 453 毫秒)
- 读取并重写类:0.87(jclasslib 917 毫秒,ASM 1050 毫秒)
- 读取、解码字节码并重写:1.23(jclasslib 1311 毫秒,ASM 1068 毫秒)
这相当了不起,因为 jclasslib 的架构要求在内存中构建出类文件结构,而在 ASM 的架构里,类文件只是“流式掠过”。当字节码本身必须解码为指令列表再重新生成时,ASM 依然领先,这正是其流式模型省去中间表示的地方。除此之外的所有场景,差距都已消失。
无需理由的性能分析
过去,性能分析需要理由。它是应用变慢或内存耗尽时你才会去做的事。有了编码智能体和 MCP 服务器,这个理由不再必要。编码智能体可以在后台修复性能问题,把应用的性能与基线进行对比,并在必要时采取行动。
不过,JProfiler MCP 服务器至关重要。没有它,编码智能体会自信满满地做出无甚益处、还常常破坏代码的“优化”。性能分析数据既指出了需要改进的地方,也提供了验证改进是否成功的方法。
欢迎到JProfiler MCP 页面试用。如果你的应用中存在通过阅读代码发现不了的热点,智能体会帮你找到它。