You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Spring Boot健康指标转为Micrometer指标时出现内存溢出错误

问题分析

你的内存泄漏问题根源在于健康指标Gauge的注册方式存在对象引用持有问题,具体来说:

  1. 注册子健康指标的Gauge时,你将healthRegistry作为观测对象传入,同时lambda表达式捕获了namedContributor。这会导致每个Gauge都持有整个HealthContributorRegistry的强引用,加上Testcontainers测试环境中频繁的容器启停、上下文刷新,这些被持有的对象无法被GC回收,最终引发OOM。
  2. 遍历healthRegistry.stream()时,没有做去重检查,可能重复注册同名Gauge,导致Meter实例数量持续增长,占用额外内存。
解决方案

调整Gauge的注册逻辑,直接使用对应的HealthIndicator实例作为观测对象,避免持有整个Registry的引用,同时优化对象捕获逻辑:

@Configuration
public class HealthApiToMetricsConvertor {

    @Bean
    public MeterRegistryCustomizer<MeterRegistry> healthRegistryCustomizer(HealthContributorRegistry healthRegistry,
                                                                           HealthEndpoint healthEndpoint) {
        return registry -> {
            // 注册根健康指标
            registry.gauge("health", Tags.of("name", "root"), healthEndpoint, 
                endpoint -> healthToCode(endpoint.health().getStatus()));

            // 注册子健康指标,直接使用对应的HealthIndicator实例
            healthRegistry.stream()
                .filter(namedContributor -> namedContributor.getContributor() instanceof HealthIndicator)
                .forEach(namedContributor -> {
                    HealthIndicator indicator = (HealthIndicator) namedContributor.getContributor();
                    String indicatorName = namedContributor.getName();
                    // 避免重复注册(可选,根据Micrometer版本可能自动去重,但显式检查更安全)
                    if (registry.find("health").tag("name", indicatorName).meter().isEmpty()) {
                        registry.gauge("health", Tags.of("name", indicatorName), indicator,
                            healthIndicator -> healthToCode(healthIndicator.getHealth(false).getStatus()));
                    }
                });
        };
    }

    private int healthToCode(Status status) {
        return Status.UP.equals(status) ? 1 : 0;
    }
}
关键优化点
  • 观测对象替换:将原有的healthRegistry替换为单个HealthIndicator实例,每个Gauge仅持有自身对应的指标实例引用,避免占用整个Registry的内存。
  • 类型过滤:增加instanceof HealthIndicator的过滤,避免处理非指标类型的HealthContributor(比如CompositeHealthContributor)。
  • 重复注册检查:显式检查是否已存在同名Gauge,防止重复创建Meter实例。
  • 简化lambda逻辑:提前获取indicator和indicatorName,减少lambda内部的对象捕获,降低引用持有风险。
额外建议
  1. 在Testcontainers测试中,确保每个测试类/方法结束后正确关闭容器并清理Spring上下文,避免上下文堆积。
  2. 可以通过Micrometer的MeterRegistry.getMeters()方法在测试中打印Meter数量,验证是否存在重复注册的情况。
  3. 启用GC日志(添加JVM参数-XX:+PrintGCDetails -XX:+PrintGCTimeStamps),确认优化后对象是否能正常被回收。

内容的提问来源于stack exchange,提问作者Nithin Varghese

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:09:55