Skip to content

[Bug] spring.index 在约 1 万个 Java 文件的项目上耗时 648 秒,且每次文件变更都会全量重跑 #299

Description

@Farewell0375

Platform / 平台

macOS

Lithe version / Lithe 版本

e19928bd (main)

OS and architecture / 操作系统与架构

macOS 15.7.3, Apple Silicon (arm64)

Environment / 环境信息

Rust: 1.94.0(release 构建实测,非 debug)
目标项目规模: 18 个 Maven 模块 / 9765 个 .java 文件 / 12611 个可见文件 / 218 MB

Steps to reproduce / 复现步骤

  1. 准备一个约 1 万个 .java 文件的多模块 Maven 项目
  2. 用 release 构建调用 lithe-core 的 spring.index 命令,payload 传入 workspace.snapshot 返回的完整 paths 列表
  3. 记录耗时

(或:在应用中打开该项目,观察 Spring 面板的索引状态持续时间)

Expected behavior / 预期行为

Spring 索引应在与项目规模成合理比例的时间内完成(该规模预期为秒级到数十秒),且不应在每次文件变更时全量重扫。

Actual behavior / 实际行为

release 构建下 spring.index 耗时 648 秒(约 11 分钟),平均每个 Java 文件 66ms。

根因是正则表达式在热路径上被反复编译。rust/lithe-core/src/languages/spring.rs 中的 bean_index每个文件的每一行都会重新编译若干正则:

// rust/lithe-core/src/languages/spring.rs:1141
fn has_annotation(context: &str, name: &str) -> bool {
    Regex::new(&format!(r"@{}(?:\s|\(|$)", regex::escape(name)))
        .unwrap()
        .is_match(context)
}

bean_index 逐行调用链上的编译次数:

调用 每行编译次数
has_component_annotation(6 个注解各一次 has_annotation 6
has_annotation(context, "Bean") 1
is_injection_context(Autowired / Inject / Resource) 3
constructor_regex(type_name)(在逐行循环体内调用) 1

即每行 11 次以上,乘以数百万行源码。

spring.rs 中共有 20 处 Regex::newendpoint_indexdeclared_supertypesparse_constructor_injectionsquoted_valuesqualifier_namescomponent_nameinjection_qualifier 等函数都存在同类问题(部分在循环体内,部分用 format! 动态拼接后编译)。

放大因素

reloadProjectServices 挂在工作区文件监听上,每批文件变更都会重新调用 AppModel.loadProjectServices,其中 springFeature.load 默认 refreshDependencyMetadata: true。也就是说保存一次文件就会触发一次全量索引。SpringFeatureModelgeneration token 丢弃过期结果,但已经派发出去的 Rust 任务没有取消机制,会跑完整个 648 秒并持续占用 CPU。

附带问题(同类,建议一并评估)

runConfig.inspect 为计算 staleFingerprint 指纹会重新遍历并 SHA-256 哈希全部 9765 个 .java 文件,单次 1.33 秒。它同样在每次项目服务重载时被调用,且没有复用工作区快照已有的文件列表。

修复方向

  • spring.rs 中的静态模式改为 LazyLock<Regex> 常量
  • constructor_regex 等按类型名拼接的正则提到逐行循环外,每个文件只构建一次
  • has_annotation 改为对固定注解集合使用预编译表

Logs or diagnostic output / 日志或诊断输出

# lithe-core release 构建,同一次运行内的相对耗时
workspace.snapshot:  elapsed=256.10ms       bytes=2632023   ok=true   files=12611
spring.index:        elapsed=648090.35ms    bytes=4862785   ok=true
runConfig.inspect:   elapsed=1325.28ms      bytes=1431623   ok=true
runConfig.resolve:   elapsed=598.18ms       bytes=18929     ok=true

# 注:本次实测 metadataRepositories 传空、refreshDependencyMetadata=false。
# 应用实际调用会传入 ~/.m2 等仓库且 refreshDependencyMetadata=true,
# 首次打开还需叠加最多 20000 个 JAR 的元数据扫描(MAX_METADATA_ARCHIVES)。

Checklist / 提交前检查

  • I searched existing issues for duplicates. / 我已搜索现有 Issue,确认没有重复报告。
  • I removed secrets and personal data from the report. / 我已从报告中移除敏感信息和个人数据。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingenhancementNew feature or requestmacossomething related to macos strongly

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions