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.