In my application I have a couple thousand lightweight objects (which I would like to keep in memory). Each lightweight object references a heavy data object which I would like to be loaded on demand and garbage collected if the heap is full (and reloaded on demand again). So using JPA I've done something like,
public class LightWeight{
@OneToOne(fetch = FetchType.LAZY)
private HeavyWeight data;
....
}
Using FetchType.LAZY works fine for loading the HeavyWeight for the first time, but unfortunately but as the HeavyWeight is a normal reference, it never gets garbage collected and thus I'am running out of memory after a while.
Is there a JPA mechanism which does lazy fetching, "un"loading if the heap becomes full (like a WeakReference) and refetches the reference again if needed?
Btw: I'am using Spring Data JPA over Hibernate as implementation.
Update 1: Considering the comment about using a second level cache, what about doing relying on the cache and detaching the heavyweight objects immediately after fetching them? I.e. something like this ....
public class LightWeight{
int heavyWeightId = ...;
@Autowired
HeavyWeightRepository heavyWeightRepository;
public HeavyWeight getData(){
HeavyWeight hv = heavyWeightRepository.findById(id);
heavyWeightRepository.detach(hv); //using custom repository
return hv;
}
}
In this case the HeavyWeight objects should be garbage collected once they are detached from the EntityManager (and are not reference from anywhere else), is this correct?
Update 2: I abandoned the idea of using SoftReferences. The main problem is that although releasing all references the entitymanager holds to the managed objects by either explicity clearing the EM or committing the transaction should allow the entities only referenced by the softreferences to be garbage collected in case memory becomes sparse, it does not work out in practice as one often runs in the "GC overhead limit exceeded" problem. So the way to go is to use a second-level cache as proposed in the comments.
答案 0 :(得分:1)
我自己没有尝试过这个,但是实现在加载时将重数据对象转换为WeakReference的JPA attribute converter呢。
对于这个用例,WeakReference似乎也很弱,可能SoftReference更好吗?
答案 1 :(得分:1)
正如ghdalum已经指出的那样,Weak或SoftReferences是在普通Java中处理这个GC问题的正确方法。
关于JPA,我发现了另外两个问题:
您是否尝试将javax.persistence.sharedCache.mode
设置为DISABLE_SELECTIVE
并在HeavyWeight课程中设置@Cacheable(false)
?
您也可以尝试使用例如
手动清除缓存javax.persistence.Cache c = myEntityManagerFactory.getCache()
c.evict(heavyWeight) // does not clear heavyWeight.references
EntityManager
。如果可能的话,我会有选择地禁用缓存,让LightWeight返回HeavyWeight的副本,这样你就可以完全控制它的生命周期(例如使用缓存)。另一个想法是在需要时使用JPQL Constructor Expressions来获取HeavyWeights数据的副本。