Spring 3.0定时任务内存泄漏问题排查求助
Spring 3.0应用内存泄漏与线程激增问题排查
问题现象
- 应用运行1天后内存额外增长1.5GB
- 调度线程数量大幅增加:
- 启动10分钟时线程状态:

- 运行1天后线程状态:

- 启动10分钟时线程状态:
问题代码
@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); } }
问题原因分析
- @EnableAsync注解误用:将该注解直接加在定时任务类上,会让Spring对类中所有方法(包括@Scheduled标注的定时任务)做异步代理。结合
fixedRate=60000的调度规则,若上一次任务未执行完成,下一次任务会被立即触发,大量异步线程被创建且无法及时回收,最终导致线程堆积。 - WebClient资源未复用:每次定时任务执行时都新建WebClient、HttpClient及SslContext实例,这些资源包含连接池、SSL上下文等重量级对象,频繁创建会导致内存占用持续攀升,且无法有效释放。
- block()调用引发线程阻塞:在定时任务中使用
block()同步等待响应,若接口响应缓慢或超时时间设置不合理,会导致线程长时间被占用。叠加fixedRate的调度逻辑,进一步加剧线程堆积。
解决方案
- 移除类上的@EnableAsync注解:若无需异步执行定时任务,直接删除该注解;若需异步,应通过
@Configuration配置类自定义TaskScheduler或AsyncConfigurer,指定线程池参数,避免无限制创建线程。 - 复用WebClient实例:将WebClient配置为单例Bean,通过依赖注入使用,而非每次任务都创建。示例配置:
之后在定时任务类中注入WebClient即可。@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(); } - 优化响应处理逻辑:尽量避免在定时任务中使用
block(),改用Reactive流式处理;若必须同步等待,确保超时时间合理,避免线程长时间阻塞。 - 调整调度策略:检查
updateOrdersList()方法的实际执行时长,若超过60秒,建议改用fixedDelay(上一次任务完成后再间隔指定时间执行),或延长fixedRate的间隔时间,避免任务叠加。
内容的提问来源于stack exchange,提问作者admin asd
相关产品推荐
相关产品推荐

