Ohhnews

分类导航

$ cd ..
foojay原文

氛围编程时代的运行时代码分析

#java#jvm-hotpath#运行时分析#代码分析#开发工具

目录 这有何不同Java 工具链的空白

“氛围编程”时代的运行时代码分析

在**“氛围编程”**时代——即在短时间内引入或重构大量代码,通常借助大语言模型(LLM)的帮助——你需要关于新逻辑实际执行情况的即时反馈。不需要全面的分析。不需要纳秒级的精确计时。只需要快速确认你的循环没有比应有的次数多运行 10,000 倍。

然而,对于快速验证而言,传统的性能分析器可能显得有些杀鸡用牛刀。此外,它们以方法/堆栈为粒度展示结果,需要进行上下文切换才能解读。它们还会引入开销,从可忽略不计(例如 JFR/采样)到显而易见(调用跟踪/插桩)不等。因此,作为快速迭代期间的常驻反馈,它们不太方便。

jvm-hotpath 是专为这种工作流程构建的轻量级 Java Agent。它直接在你的源代码中显示每行执行计数,确切地告诉你哪些行运行了以及运行频率——就在你的应用程序运行时。

这有何不同

零计时开销。 仅计数,不进行纳秒级测量。

统计每次执行。 无采样,不会遗漏快速方法。

对 LLM 友好。 你可以将其传输给 LLM 进行分析的 JSON 报告。

实时更新。 JSONP 轮询让你可以实时观察计数更新。无需服务器。

现代 Java。 在 CI 中针对 Java 11、17、21、23 和 24 进行了测试。它也适用于 Spring Boot 和 Micronaut。

Java 工具链的空白

最初的问题

直接的痛点很简单:代码生成的速度超过了你构建心智模型的速度。几年前,我在一个继承的系统中遇到了同样的核心问题。所以我改造了 Cobertura——一个覆盖率工具——将其用作运行时分析工具。通过对应用程序进行插桩并执行特定行为,我可以在事后观察执行计数。结果,我得到了代码库的运行时形态心智地图——以及一个可以自信地进行更改的切入点。

毕竟,静态分析告诉你可能执行什么。测试告诉你应该执行什么。我需要的是看到在实际工作负载下确实执行了什么。

现有工具为何不适用

Cobertura 的最后一个版本发布于 2015 年,因此它不适合现代 Java 工具链。此后,没有广泛采用且积极维护的工具专注于实时的每行执行频率。

覆盖率工具(例如 JaCoCo)跟踪代码是否执行,而不是执行了多少次。性能分析器显示 CPU 时间消耗在哪里。两者都无法显示实际条件下的执行频率。

我最终为何决定构建它

现代 Java 工具链已经朝着不同的方向发展,但这个想法一直萦绕在我心头。所以我评估了可用的工具。例如,OpenClover 的“完全支持”版本是 Java 17,较新版本被列为实验性版本。同样,JCov 作为 OpenJDK CodeTools 项目存在,但设置比较老派。简而言之,没有简单的“从 Maven Central 拉取一个 jar 包就能用”的路径。IntelliJ 的内置覆盖率功能对于覆盖率来说非常出色,它将运行数据存储为 IDE 覆盖率套件(例如 .ic)。然而,它仍然是 IDE 中心的工作流程。简而言之,这不是你可以在 CI 构件中重用或作为独立实时报告共享的东西。

在一个小时的死胡同之后,Claude 直奔主题:

“你想让我帮你从源代码构建 JCov,还是我为你创建一个简单的自定义执行计数器?”

最终,那个问题决定了方向。

真实世界的 Bug

Bug 是如何出现的

这个工具诞生于一次高速的“氛围编程”会话中。具体来说,我正在重构一个核心处理引擎。标准的性能分析器遗漏了这个 Bug。系统感觉还不慢:

Bug: 一个 .filter(r -> r.isDuplicate()) 调用在 15 秒内执行了 1900 万次。

问题: 每次调用约 50 纳秒——采样分析器很容易将其采样不足。

影响: O(N²) 而不是 O(1) 藏在眼皮底下。

为何难以发现

换句话说,过滤器位于循环内部,而不是只计算一次。这是一个经典的错误。然而,传统工具无法看到它。相反,我希望对实际运行的内容获得即时的运行时可见性。

执行计数让这一点变得显而易见。例如,在单行代码旁边看到“19,147,293 次执行”消除了所有歧义。不需要计时数据,也不需要解读。

核心洞察:频率 ≠ 资源消耗

Java 性能分析器专注于资源消耗:CPU 时间、内存分配、线程争用。相比之下,jvm-hotpath 显示代码运行了多少次(频率)。

在现代 Java 中,这种区别很重要。例如,JIT 编译使单个调用变得很快。因此,瓶颈通常是算法层面的——O(N) 与 O(1) 的区别。此外,逻辑错误可能会产生数百万次不必要的调用。而且,采样分析器是基于统计学的。此外,非常短但频繁的工作很容易被采样不足。

它是“逻辑 X 光”,而不是“资源监视器”。

工作原理

插桩

jvm-hotpath 是一个 Java Agent,它在类加载时使用 ASM 对字节码进行插桩。具体来说,它在每个可执行行之前插入一个计数器。确实,没有采样,没有计时——只有频率。

因此,开销足够低,适合正常的开发运行。

报告

收集的数据被写入一个交互式 HTML 报告,该报告在你的应用程序运行时刷新。具体来说,它显示带有语法高亮的源代码,每行旁边都有执行计数。此外,全局热力图使热路径在视觉上更加突出。

通过 JSONP 驱动的轮询,你可以直接从磁盘(file://)打开报告并实时观察其更新。无需服务器。

值得注意的是,这种狭窄的关注是故意的。没有火焰图,没有仪表板,没有事后跟踪——只有映射到源代码的行级执行频率。

机器可读输出

Agent 还会写入 execution-report.json。因此,它为你提供了一个机器可读的构件,你可以将其输入到 CI 步骤或基于 LLM 的工具中。

实际效果演示:

https://github.com/user-attachments/assets/cc89451b-a41f-491e-a1f6-8e87328979c0

入门指南

Maven 插件(推荐)

将插件添加到你的 pom.xml

$ xml
<plugin>
    <groupId>io.github.sfkamath</groupId>
    <artifactId>jvm-hotpath-maven-plugin</artifactId>
    <version>0.2.4</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- 每 5 秒自动刷新报告 -->
        <flushInterval>5</flushInterval>
    </configuration>
</plugin>

然后使用激活的 agent 运行你的应用程序:

$ bash
mvn -Pinstrument jvm-hotpath:prepare-agent exec:exec

对于 exec:exec,你需要一个主类。传递 -Dexec.mainClass=... 或在 pom.xml 中配置 exec.mainClass

报告将在 target/site/jvm-hotpath/execution-report.html 生成。

对于多模块项目或生成的代码(OpenAPI/MapStruct),插件可以将多个源根合并到一个报告中。此外,你可以通过 sourcepath 直接传递依赖源代码存档。

手动使用 Agent

如果你更喜欢直接控制,请运行:

$ bash
java -javaagent:jvm-hotpath-agent.jar=packages=com.example,sourcepath=src/main/java,flushInterval=5 -jar your-app.jar

关键参数: packages 设置要插桩的包。sourcepath 指向源根或存档(.jar.zip)。flushInterval 控制报告刷新之间的秒数(0 = 不自动刷新)。verbose 打印带有可点击文件 URL 的插桩详细信息。

独立重新生成报告

如果你已经有来自 CI 的 execution-report.json,则可以在不重新运行应用程序的情况下重新生成 HTML:

$ bash
java -jar jvm-hotpath-agent.jar --data=target/site/jvm-hotpath/execution-report.json --output=target/site/jvm-hotpath/new-report.html

这不是什么

它不是覆盖率百分比工具——请使用 JaCoCo。它也不是 CPU 计时分析器——请使用 JFR 或 async-profiler。最后,它不是 24/7 生产监控系统。

超越性能:死代码与认知负荷

执行计数使发现死代码和很少使用的分支变得容易。此外,它们揭示了主要出于历史原因而存在的功能。而且,它们减少了认知负荷。当你知道哪些部分实际运行时,推理更改、自信重构或决定暂时不考虑什么就会变得容易得多。

确实,对于任何使用 AI 辅助工具快速工作的人来说,这种清晰度是非常宝贵的。

关于构建方式的说明

第一个原型诞生于 AI 辅助的“氛围编程”,主要是与 Claude 一起。随后,我结合了手动工作以及 Codex 和 Gemini 的帮助进行了迭代。我还针对真实的 JVM 工作负载验证了一切。

总的来说,这些工具加速了探索。尽管如此,动机和方向来自于真实代码库中的实际使用。

未来方向

有明显的下一步——Gradle 改进、更好的排除控制、更广泛的框架测试。不过就目前而言,我故意保持范围较小。确实,这是我的第一个开源版本。

真正的问题更简单:这是否能帮助你更快、更自信地理解你的代码库?


项目: github.com/sfkamath/jvm-hotpath

文档: 完整 README

动机: 深入了解原因

本文 Runtime Code Analysis in the Age of Vibe Coding 首次发布于 foojay