JPA 的软引用

SoftReferences for JPA

在我的应用程序中,我有几千个轻量级对象(我想将它们保存在内存中)。每个轻量级对象都引用一个重数据对象,如果堆已满(并再次按需重新加载),我希望按需加载并收集垃圾。所以使用 JPA 我做了类似的事情,

public class LightWeight{
    @OneToOne(fetch = FetchType.LAZY)
    private HeavyWeight data;
    ....
}

第一次使用 FetchType.LAZY 加载 HeavyWeight 效果很好,但不幸的是,由于 HeavyWeight 是一个普通引用,它永远不会被垃圾收集,因此我 运行一段时间后的记忆。

是否有一种 JPA 机制可以延迟获取,"un"在堆变满时加载(如 WeakReference)并在需要时再次重新获取引用?

顺便说一句:我正在使用 Spring Data JPA over Hibernate 作为实现。

更新一: 考虑到关于使用二级缓存的评论,依赖缓存并在获取它们后立即分离重量级对象怎么样? IE。像这样....

public class LightWeight{

        int heavyWeightId = ...;

        @Autowired
        HeavyWeightRepository heavyWeightRepository;

        public HeavyWeight getData(){
            HeavyWeight hv = heavyWeightRepository.findById(id);
            heavyWeightRepository.detach(hv); //using custom repository
            return hv;
        }
  }

在这种情况下,一旦 HeavyWeight 对象与 EntityManager 分离(并且未从其他任何地方引用),它们应该被垃圾收集,这是否正确?

更新2:我放弃了使用SoftReferences的想法。主要问题是,尽管通过显式清除 EM 或提交事务来释放实体管理器对托管对象的所有引用,应该允许仅由软引用引用的实体在内存变得稀疏的情况下被垃圾收集,但它不起作用在实践中,经常遇到 "GC overhead limit exceeded" 问题。所以要走的路是使用评论中建议的二级缓存。

我自己还没有尝试过,但是实现一个 JPA attribute converter 在加载时将重数据对象转换为 WeakReference 怎么样。

对于这个用例,WeakReference 似乎也很弱,也许 SoftReference 更好?

正如 ghdalum 已经指出的那样,Weak 或 SoftReferences 是处理此 GC 问题的正确方法 Java。

关于 JPA,我看到另外两个可能的问题:

  1. 持久性提供程序的缓存(他们也可能使用 SoftReferences)
  2. 保持实体由您的持久性上下文管理

1

您是否尝试过将 javax.persistence.sharedCache.mode 设置为 DISABLE_SELECTIVE 并在您的重量级 class 上设置 @Cacheable(false)

您也可以尝试手动清除缓存,例如

javax.persistence.Cache c = myEntityManagerFactory.getCache()
c.evict(heavyWeight)  // does not clear heavyWeight.references

2

  • 分离实体或
  • 关闭 EntityManager.

如果可能,我会选择性地禁用缓存并让 LightWeight return 一个 HeavyWeight 的副本,这样您就可以完全控制它的生命周期(例如使用缓存)。另一个想法是在需要时使用 JPQL Constructor Expressions 获取 HeavyWeights 数据的副本。