Spring Boot内嵌内存H2DB内存泄漏问题求助
内存H2DB循环删插后OOM问题(MVStore内存泄漏解决方案)
问题背景
使用Spring Boot 2.7.1 + Spring Data JPA 2.7.1 + H2 2.1.212内嵌内存数据库,保持最新10万条记录,持续删除旧数据并插入新数据。运行4-5小时后触发OOM,删除记录后内存占用仍持续增长;MAT堆分析显示org.h2.mvstore.tx.TransactionStore$TxMapBuilder$TMVMap实例占用77.95%堆内存,内存集中在org.h2.mvstore.Page$PageReference[];重启应用后相同记录数仅占用30%内存。
解决方案
1. 强制MVStore页压缩与清理
H2的MVStore默认不会主动回收已删除数据占用的页,需手动触发压缩释放内存。建议在每次批量删除后执行:
-- 针对单表压缩 ALTER TABLE YOUR_TABLE_NAME COMPRESS; -- 全局压缩所有MVStore数据 CALL COMPRESS();
2. 调整H2数据源配置参数
修改JDBC URL,添加MVStore优化参数:
jdbc:h2:mem:db;DB_CLOSE_ON_EXIT=FALSE;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;MV_STORE=TRUE;MVCC=FALSE;MV_AUTO_COMPRESS=TRUE;MV_PAGE_SIZE=16384;MV_MAX_PAGE_COUNT=100000
MV_AUTO_COMPRESS=TRUE:开启自动压缩,当页使用率低于阈值时自动触发MV_PAGE_SIZE=16384:调大页大小(默认4k),减少总页数量MV_MAX_PAGE_COUNT:限制最大页数量,超过后强制清理无用页
3. 排查并关闭长事务
未提交的长事务会导致MVStore保留旧版本数据,无法释放内存。确保所有数据库操作都在短事务内完成,及时提交或回滚,避免事务长时间持有资源。
4. 升级H2版本
H2 2.1.x版本存在MVStore内存泄漏已知问题,建议升级至2.2.x及以上稳定版,官方已修复该场景下的部分内存泄漏问题。
5. 主动触发内存回收
通过H2 API主动执行检查点与压缩,在Spring Boot中可通过数据源调用:
try (Connection conn = dataSource.getConnection()) { Statement stmt = conn.createStatement(); stmt.execute("CALL COMPRESS()"); stmt.execute("CALL CHECKPOINT()"); } catch (SQLException e) { // 异常处理逻辑 }
CHECKPOINT()会强制刷写内存数据并清理无用页。
6. 定时重建表(兜底方案)
若上述方案无效,可定期重建表彻底清理内存:
- 创建临时表,插入最新10万条记录
- 重命名原表后删除
- 将临时表重命名为原表名
内容的提问来源于stack exchange,提问作者Arjun Sahoo
相关产品推荐
相关产品推荐

