views:

1215

answers:

10

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?

+1  A: 

Garbage collection and other references like soft references are non deterministic this it's not really possible to reliable do stuff so that soft references are definitely cleared at that point so your test can judge how yourcache reacts. I would suggest you simulate the reference clearing in more definite way by mocking etc - your tests will be reproducable and more valuable rather than just Hopi g for the GC to clean up references. Using the latter approach is a really bad thing to do and willjust introduce additional problems rather than help you improve the quality of your cache and it's collaborating components.

mP
But then again the reference-clearing at OOM-time is defined behaviour and is pretty much what he likes to achieve with the SoftReferences (if I understand correctly), so I'd say he should test it.
Joachim Sauer
A: 

From the documentation and my experience I'd say yes: you must have a reference somewhere else.

I'd suggest using a debugger that can show you all references to an object (such as Eclipse 3.4 when debugging Java 6) and just check when the OOM is thrown.

Joachim Sauer
+7  A: 

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.

Andrzej Doyle
+1 for jhat reference, looks like a very useful tool. Is there anything like this for .NET?
Wim Coenen
Wim Coenen
Ever better than jhat is Eclipse's Memory Analyzer: http://www.eclipse.org/mat
Epaga
A: 

If you use eclipse, there is this tool named Memory Analyzer that makes heap dump debugging easier.

jassuncao
+1  A: 

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.

James
+1  A: 

In a typical JVM implementation (SUN) you need to trigger a Full GC more than once to get the Softreferences cleaned. The reason for that is because Softreferences require the GC to do more work, because for example of a mechanism that allows you to get notified when the objects are reclaimed.

IMHO using a lot of sofreferences in an application server is evil, because the developer has not much control over when they are released.

kohlerm
Agree with the first point (correct, the first time it only takes note of the clock). Disagree with the second point - too general.
jgubby
Why do you think it's too general. Ok if you have only one application on your server than it is maybe. But I think that's very rare. And even then the policy is often not what you would want.
kohlerm
+2  A: 

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.

Luke Quinane
A: 

If you have a cache which is a Map of SoftReferences and you want them cleared you can just clear() the map and they will all be cleaned up (including their references)

Peter Lawrey
-1. No this is not the same as clearing the references. It only causes the SoftReference to become unreachable (as long as there are no other strong references to the SoftReference.) If the SoftReference is gc'ed as a result then it will not be enqueued. If you were using the ReferenceQueue to notify you when the SoftReference was cleared then this will prevent the cleanup method being called.
finnw
A: 

Does the cached object have a finalizer? The finalizer will create new strong references to the object, so even if the SoftReference is cleared the memory will not be reclaimed until a later GC cycle

finnw
A: 

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

David Gageot