Hibernate缓存导致内存溢出。缓存中仍缓存着已删除的实体,未显示

后端开发 2026-07-11

我已经解决了我的问题。我只是并不完全理解为什么Hibernate没有显示它的缓存条目。

我曾有如下实现,在删除大量实体时导致我的服务出现内存溢出。

    Lists.partition(bigIdList, 10000).forEach(idsToDelete-> {
            myRepository.deleteAllById(idsToDelete);
            log.info("Deleted " + idsToDelete.size() + " myEntitiy Entries");
    });

我理解我的错误。"deleteAllById" 会加载一个实体。我显式地没有使用 @Transactional注解。该逻辑是由一个REST调用触发的。这段逻辑导致我的服务的内存持续增长,直到抛出内存溢出错误。我修改了

deleteAllById --> deleteAllByIdInBatch

从而执行以下代码:

    Lists.partition(bigIdList, 10000).forEach(idsToDelete-> {
            myRepository.deleteAllByIdInBatch(idsToDelete);
            log.info("Deleted " + idsToDelete.size() + " myEntitiy Entries");
    });

现在实现按预期工作。

我的问题现在是,为什么在Hibernate缓存中看不到已加载的实体。我通常通过以下方式查看Hibernate缓存:

Map.Entry<Object, EntityEntry>[] entries = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().reentrantSafeEntityEntries();

然而,在使用

    Lists.partition(bigIdList, 10000).forEach(idsToDelete-> {
            myRepository.deleteAllById(idsToDelete);
            log.info("Deleted " + idsToDelete.size() + " myEntitiy Entries");
            Map.Entry<Object, EntityEntry>[] entries =       entityManager.unwrap(SessionImplementor.class).getPersistenceContext().reentrantSafeEntityEntries();
    });

时,这始终是空的。

那么我的内存泄漏到底来自哪里?我并不确切知道应该去哪里查找。我刚测试过,它“起作用”了,因为我的内存不再上升。我还需要查看Hibernate的其他缓存实例吗?

解决方案

缺失的部分是这样的:

没有 @Transactional 时,对 deleteAllById() 的每次调用都在自己的短暂Hibernate会话中执行。所以没有一个长期运行的事务,Hibernate的持久化上下文是在每次方法调用时创建并销毁的,而不是在循环的每次迭代中。如果你要删除成千上万的ID,就意味着成千上万的会话被创建并销毁。

尽管每个会话都快速关闭,Hibernate在加载待删除的实体时仍然会分配大量临时对象。因此对于每个批次:

  • 加载最多10,000个实体
  • 删除它们
  • flush
  • 关闭会话
  • 垃圾回收器不能立即回收内存,因为:

  • 循环仍在运行

  • 你的JVM堆内存压力增大
  • Hibernate实际上在分配大量临时对象(hydration状态、SQL字符串、JDBC缓冲区、代理、元数据查找等)

这些分配保留的时间足够长,推动堆内存上升,而JVM的垃圾回收器无法跟上新分配的速度,导致内存不断增加。这就是为什么在你检查时,一级缓存总是显示为空——因为会话已经结束,它们只是临时对象——但内存仍在上升。

这会导致短lifespan但规模较大的分配持续积累,最终把堆推向OOM的边缘。

如果你在JVM上使用分析工具,在循环运行时,应该能看到与你删除对象相关的分配峰值,例如:Object[] 数组、byte[]EntityEntry 以及 EntityKey 实例等。分析工具会显示,导致OOM的其实是重复的实体hydration(填充/加载)分配的峰值,而不是缓存泄漏。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章