Spring Batch内存占用过高求助:处理20万条记录时内存持续上涨未释放
Spring Batch内存与性能问题解决方案
核心根因定位
你遇到的内存持续上涨、执行速度越来越慢的问题,90%以上是Hibernate/JPA的一级缓存泄漏导致的:Spring Batch默认使用的JPA事务会绑定EntityManager,所有读取、写入过的实体都会被缓存在EntityManager的一级缓存中,不会被GC回收,缓存实体越多内存占用越高,且每次事务flush时的脏检查耗时会线性增长,直接导致执行速度骤降。
具体修复方案
- 新增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缓存。
- 新增Chunk处理后的缓存清理逻辑
- 修正Reader的作用域配置
你的OptInItemReaderTest默认是单例Bean,多个Step共用会导致状态残留、上一步的资源无法释放,添加@StepScope注解,每个Step独立创建Reader实例,Step执行完后实例会被自动回收:
@Component @StepScope public class OptInItemReaderTest extends RepositoryItemReader<BodiCmMemberEntity> { // 原有逻辑不变 }同理,Processor、Writer如果有Step级的状态,也建议加上
@StepScope注解。- 修正Reader的作用域配置
- 对齐chunk与pageSize配置
你最初的chunk大小为5、pageSize为100,相当于每读取100条数据要处理20次事务,效率极低,建议将chunk大小调整为和pageSize一致(比如都设置为100),减少事务提交次数,也更利于内存回收。
- 对齐chunk与pageSize配置
- 优化实体关联加载策略
你在Processor中用到了关联对象PoleEditionCodeEntity,如果该关联配置为FetchType.EAGER急加载,每次读取主实体都会连带加载关联对象,内存占用直接翻倍:
- 将关联改为
FetchType.LAZY懒加载 - 或者修改
findAllOptIns查询方法,用JOIN FETCH只查询需要的editionCode字段,避免加载整个关联实体
- 优化实体关联加载策略
- 堆快照验证(可选)
若仍需确认根因,给JVM添加启动参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof,OOM时导出堆快照,用MAT工具分析,可直接定位到占用内存最大的对象的持有引用链。
- 堆快照验证(可选)
额外性能优化建议
如果调整后仍有性能问题,可以尝试将RepositoryItemReader替换为JdbcCursorItemReader/JdbcPagingItemReader,直接用JDBC读取数据,跳过Hibernate的缓存机制,内存占用会低很多,执行速度也会提升30%以上。
内容的提问来源于stack exchange,提问作者Trace
相关产品推荐
相关产品推荐

