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

Spring 3.0定时任务内存泄漏问题排查求助

Spring 3.0应用内存泄漏与线程激增问题排查

问题现象

  • 应用运行1天后内存额外增长1.5GB
  • 调度线程数量大幅增加:
    • 启动10分钟时线程状态:10分钟时线程状态
    • 运行1天后线程状态:1天后线程状态

问题代码

@EnableAsync
@Component
public class ScheduleFixedRate {

    SellerRepository sellerRepository;
    SellerProfileRepository sellerProfileRepository;
    StatsRepository statsRepository;

    public ScheduleFixedRate(SellerRepository sellerRepository, SellerProfileRepository sellerProfileRepository, StatsRepository statsRepository) {
        this.sellerRepository = sellerRepository;
        this.sellerProfileRepository = sellerProfileRepository;
        this.statsRepository = statsRepository;
    }


    public Builder webClientBuilder(String baseUrl) {
        boolean secure = baseUrl.contains("https");

        HttpClient httpClient = HttpClient.create().followRedirect(true);
        if (secure) {
            httpClient = httpClient.secure(sslContextSpec -> sslContextSpec.sslContext(createInsecureSslContext()));
        }
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .baseUrl(baseUrl);
    }

    private SslContext createInsecureSslContext() {
        try {
            return SslContextBuilder
                    .forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE)
                    .build();
        } catch (SSLException e) {
            throw new RuntimeException(e);
        }
    }

    @Scheduled(fixedRate = 60000)
    public void updateOrdersList() {
        WebClient client = webClientBuilder("https://web.com:443").build();
        HttpMethod method = HttpMethod.valueOf("GET");
        RequestBodySpec requestBodySpec = client.method(method).uri("/api***");
        requestBodySpec.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36");
        StringBuilder response = new StringBuilder();

        try {
            response.append(requestBodySpec.exchangeToMono(clientResponse -> {
                if (clientResponse.statusCode().is4xxClientError()) {
                    return Mono.error(new Throwable());//Mono.empty();
                }
                return clientResponse.bodyToMono(String.class);
            }).block(Duration.ofSeconds(5)));
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        // doing some manipulation with response
        sellerRepository.save(data);
    }
}

问题原因分析

  1. @EnableAsync注解误用:将该注解直接加在定时任务类上,会让Spring对类中所有方法(包括@Scheduled标注的定时任务)做异步代理。结合fixedRate=60000的调度规则,若上一次任务未执行完成,下一次任务会被立即触发,大量异步线程被创建且无法及时回收,最终导致线程堆积。
  2. WebClient资源未复用:每次定时任务执行时都新建WebClient、HttpClient及SslContext实例,这些资源包含连接池、SSL上下文等重量级对象,频繁创建会导致内存占用持续攀升,且无法有效释放。
  3. block()调用引发线程阻塞:在定时任务中使用block()同步等待响应,若接口响应缓慢或超时时间设置不合理,会导致线程长时间被占用。叠加fixedRate的调度逻辑,进一步加剧线程堆积。

解决方案

  • 移除类上的@EnableAsync注解:若无需异步执行定时任务,直接删除该注解;若需异步,应通过@Configuration配置类自定义TaskScheduler或AsyncConfigurer,指定线程池参数,避免无限制创建线程。
  • 复用WebClient实例:将WebClient配置为单例Bean,通过依赖注入使用,而非每次任务都创建。示例配置:
    @Bean
    public WebClient webClient() {
        String baseUrl = "https://web.com:443";
        boolean secure = baseUrl.contains("https");
        HttpClient httpClient = HttpClient.create().followRedirect(true);
        if (secure) {
            httpClient = httpClient.secure(sslContextSpec -> sslContextSpec.sslContext(createInsecureSslContext()));
        }
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .baseUrl(baseUrl)
                .defaultHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36")
                .build();
    }
    
    之后在定时任务类中注入WebClient即可。
  • 优化响应处理逻辑:尽量避免在定时任务中使用block(),改用Reactive流式处理;若必须同步等待,确保超时时间合理,避免线程长时间阻塞。
  • 调整调度策略:检查updateOrdersList()方法的实际执行时长,若超过60秒,建议改用fixedDelay(上一次任务完成后再间隔指定时间执行),或延长fixedRate的间隔时间,避免任务叠加。

内容的提问来源于stack exchange,提问作者admin asd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 05:15:40