I am using a search library which advises keeping search handle object open for this can benefit query cache. Over the time I have observed that the cache tends to get bloated (few hundred megs and keeps growing) and OOMs started to kick in. There is no way to enforce limits of this cache nor plan how much memory it can use. So I have increased the Xmx limit, but that's only a temporary solution to the problem.
Eventually I am thinking to make this object a referent of java.lang.ref.SoftReference
. So if the system runs low on free memory, it would let the object go and a new one would be created on demand. This would decrease some speed after fresh start, but this is a much better alternative than hitting OOM.
The only problem I see about SoftReferences is that there is no clean way of getting their referents finalized. In my case, before destroying the search handle I need to close it, otherwise the system might run out of file descriptors. Obviously, I can wrap this handle into another object, write a finalizer on it (or hook onto a ReferenceQueue/PhantomReference) and let go. But hey, every single article in this planet advises against using finalizers, and especially - against finalizers for freeing file handles (e.g. Effective Java ed. II, page 27.).
So I am somewhat puzzled. Should I carefully ignore all these advices and go on. Otherwise, are there any other viable alternatives? Thanks in advance.
EDIT #1: Text below was added after testing some code as suggested by Tom Hawtin. To me, it appears that either suggestion isn't working or I am missing something. Here's the code:
class Bloat { // just a heap filler really
private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
private final int ii;
public Bloat(final int ii) {
this.ii = ii;
}
}
// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
private final T hardRef;
MyReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.hardRef = referent;
}
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
int i=0;
while(i<50000) {
// set.add(new MyReference<Bloat>(new Bloat(i), rq));
set.add(new SoftReference<Bloat>(new Bloat(i), rq));
// MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();
if (polled != null) {
Bloat polledBloat = polled.get();
if (polledBloat == null) {
System.out.println("is null :(");
} else {
System.out.println("is not null!");
}
}
i++;
}
}
If I run the snippet above with -Xmx10m
and SoftReferences (as in code above), I'm getting tons of is null :(
printed. But if I replace the code with MyReference
(uncommenting two lines with MyReference and commenting out ones with SoftReference) I always get OOM.
As I understood from the advice, having hard reference inside MyReference
should not prevent object hitting ReferenceQueue
, right?