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

Spring Batch内存占用过高求助:处理20万条记录时内存持续上涨未释放

Spring Batch内存与性能问题解决方案

核心根因定位

你遇到的内存持续上涨、执行速度越来越慢的问题,90%以上是Hibernate/JPA的一级缓存泄漏导致的:Spring Batch默认使用的JPA事务会绑定EntityManager,所有读取、写入过的实体都会被缓存在EntityManager的一级缓存中,不会被GC回收,缓存实体越多内存占用越高,且每次事务flush时的脏检查耗时会线性增长,直接导致执行速度骤降。

具体修复方案

    1. 新增Chunk处理后的缓存清理逻辑
      自定义Writer或添加ChunkListener,每次chunk处理完成后手动清空EntityManager的一级缓存:
    @Component
    public class MutationHistoryItemWriter extends RepositoryItemWriter<AbstractMutationHistoryEntity>{
        @PersistenceContext
        private EntityManager entityManager;
    
        public MutationHistoryItemWriter(MutationHistoryRepository mutationHistoryRepository) {
            setRepository(mutationHistoryRepository);
        }
    
        @Override
        public void write(List<? extends AbstractMutationHistoryEntity> items) throws Exception {
            super.write(items);
            // 手动flush并清空一级缓存
            entityManager.flush();
            entityManager.clear();
        }
    }
    

    所有用到RepositoryItemReader的Step也可以添加ChunkListener,每次读完chunk后清空Reader对应的EntityManager缓存。

    1. 修正Reader的作用域配置
      你的OptInItemReaderTest默认是单例Bean,多个Step共用会导致状态残留、上一步的资源无法释放,添加@StepScope注解,每个Step独立创建Reader实例,Step执行完后实例会被自动回收:
    @Component
    @StepScope
    public class OptInItemReaderTest extends RepositoryItemReader<BodiCmMemberEntity> {
        // 原有逻辑不变
    }
    

    同理,Processor、Writer如果有Step级的状态,也建议加上@StepScope注解。

    1. 对齐chunk与pageSize配置
      你最初的chunk大小为5、pageSize为100,相当于每读取100条数据要处理20次事务,效率极低,建议将chunk大小调整为和pageSize一致(比如都设置为100),减少事务提交次数,也更利于内存回收。
    1. 优化实体关联加载策略
      你在Processor中用到了关联对象PoleEditionCodeEntity,如果该关联配置为FetchType.EAGER急加载,每次读取主实体都会连带加载关联对象,内存占用直接翻倍:
    • 将关联改为FetchType.LAZY懒加载
    • 或者修改findAllOptIns查询方法,用JOIN FETCH只查询需要的editionCode字段,避免加载整个关联实体
    1. 堆快照验证(可选)
      若仍需确认根因,给JVM添加启动参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,OOM时导出堆快照,用MAT工具分析,可直接定位到占用内存最大的对象的持有引用链。

额外性能优化建议

如果调整后仍有性能问题,可以尝试将RepositoryItemReader替换为JdbcCursorItemReader/JdbcPagingItemReader,直接用JDBC读取数据,跳过Hibernate的缓存机制,内存占用会低很多,执行速度也会提升30%以上。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 00:06:00