Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cause soft references to be cleared in Java?

I have a cache which has soft references to the cached objects. I am trying to write a functional test for behavior of classes which use the cache specifically for what happens when the cached objects are cleared.

The problem is: I can't seem to reliably get the soft references to be cleared. Simply using up a bunch of memory doesn't do the trick: I get an OutOfMemory before any soft references are cleared.

Is there any way to get Java to more eagerly clear up the soft references?


Found here:

"It is guaranteed though that all SoftReferences will get cleared before OutOfMemoryError is thrown, so they theoretically can't cause an OOME."

So does this mean that the above scenario MUST mean I have a memory leak somewhere with some class holding a hard reference on my cached object?

like image 370
Epaga Avatar asked Jan 19 '09 10:01

Epaga


4 Answers

The problem is: I can't seem to reliably get the soft references to be cleared.

This is not unique to SoftReferences. Due to the nature of garbage collection in Java, there is no guarantee that anything that is garbage-collectable will actually be collected at any point in time. Even with a simple bit of code:

Object temp = new Object();
temp = null;
System.gc();

there is no guarantee that the Object instantiated in the first line is garbage collected at this, or in fact any point. It's simply one of the things you have to live with in a memory-managed language, you're giving up declarative power over these things. And yes, that can make it hard to definitively test for memory leaks at times.


That said, as per the Javadocs you quoted, SoftReferences should definitely be cleared before an OutOfMemoryError is thrown (in fact, that's the entire point of them and the only way they differ from the default object references). It would thus sound like there is some sort of memory leak in that you're holding onto harder references to the objects in question.

If you use the -XX:+HeapDumpOnOutOfMemoryError option to the JVM, and then load the heap dump into something like jhat, you should be able to see all the references to your objects and thus see if there are any references beside your soft ones. Alternatively you can achieve the same thing with a profiler while the test is running.

like image 114
Andrzej Doyle Avatar answered Nov 02 '22 08:11

Andrzej Doyle


There is also the following JVM parameter for tuning how soft references are handled:

-XX:SoftRefLRUPolicyMSPerMB=<value>

Where 'value' is the number of milliseconds a soft reference will remain for every free Mb of memory. The default is 1s/Mb, so if an object is only soft reachable it will last 1s if only 1Mb of heap space is free.

like image 20
Luke Quinane Avatar answered Nov 02 '22 07:11

Luke Quinane


You can force all SoftReferences to be cleared in your tests with this piece of code.

like image 5
David Gageot Avatar answered Nov 02 '22 06:11

David Gageot


If you really wanted to, you can call clear() on your SoftReference to clear it.

That said, if the JVM is throwing an OutOfMemoryError and your SoftReference has not been cleared yet, then this means that you must have a hard reference to the object somewhere else. To do otherwise would invalidate the contract of SoftReference. Otherwise, you are never guaranteed that the SoftReference is cleared: as long as there is still memory available, the JVM does not need to clear any SoftReferences. On the other hand, it is allowed to clear them next time it does a GC cycle, even if it doesn't need to.

Also, you can consider looking into WeakReferences since the VM tends to be more aggressive in clear them. Technically, the VM isn't ever required to clear a WeakReference, but it is supposed to clean them up next time it does a GC cycle if the object would otherwise be considered dead. If your are trying to test what happens when your cache is cleared, using WeakReferences should help your entries go away faster.

Also, remember that both of these are dependent on the JVM doing a GC cycle. Unfortunately, there is no way to guarantee that one of those ever happens. Even if you call System.gc(), the garbage collector may decide that it is doing just peachy and choose to do nothing.

like image 2
James Avatar answered Nov 02 '22 06:11

James