What is the proper approach to testing a weak reference in Java?
My initial idea is to do the following:
public class WeakReferenceTest {
public class Target{
private String value;
public Target(String value){
this.value = value;
}
public String toString(){
return value;
}
}
public class UsesWeakReference{
WeakReference<Target> reference;
public UsesWeakReference(Target test){
reference = new WeakReference<Target>(test);
}
public String call(){
Target test = reference.get();
if(test != null){
return test.toString();
}
return "empty";
}
}
@Test
public void testWeakReference(){
Target target = new Target("42");
UsesWeakReference usesWeakReference = new UsesWeakReference(target);
WeakReference<Target> triggerReference = new WeakReference<Target>(target);
assertEquals("42", usesWeakReference.call());
target = null;
while(triggerReference.get() != null){
System.gc();
}
assertEquals("empty", usesWeakReference.call());
}
}
The reservation I have about the approach is using System.gc(), as I understand that it can behave differently on different JVMs.
A weak reference, simply put, is a reference that isn't strong enough to force an object to remain in memory. Weak references allow you to leverage the garbage collector's ability to determine reachability for you, so you don't have to do it yourself.
The WeakReference is a reference type and so, when you allocate a WeakReference you are allocating an entire object (with a finalizer too) to reference another object. Only that other object will be "weakly referenced". So it is usually not recommended to use WeakReference s to reference small data.
WeakReference: a weak reference is a reference not strong enough to keep the object in memory. If we try to determine if the object is strongly referenced and it happened to be through WeakReferences, the object will be garbage-collected.
WeakReference: is used to hold an object which will become eligible for the garbage collection as soon as it is not reachable by the program. SoftReference: lives longer, it will only be garbage collected before an OutOfMemoryError is thrown.
There's no 100% bombproof way of testing code that uses the Reference types. The behaviour of Reference objects depends on when the GC runs, and there is no 100% reliable way of forcing the GC to run.
The best you can do is:
System.gc()
is a no-op OR be willing to disable or skip the test, or ignore the test failure.(You should be able to detect that System.gc()
is being ignored by looking at how much memory is in use before and after the call; e.g. by calling Runtime.totalMemory()
)
Actually, there is another "solution". Have your unit test generate a huge amount of garbage ... enough to guarantee that you will trigger garbage collection. (Not a good idea, IMO.)
New answer to old question; I found your question as I am dealing with the exact same problem: I want to write a unit test in order to verify that my class under test does something very specific if the referent of a WeakReference turns null.
I first wrote a simple test case that would set the referent to null; to then call System.gc()
; and interestingly enough: at least within my eclipse, that was "good enough" for my weakRefernce.get()
to return null.
But who knows if that will work for all the future environments that will run this unit test for the years to come.
So, after thinking some more:
@Test
public void testDeregisterOnNullReferentWithMock() {
@SuppressWarnings("unchecked")
WeakReference<Object> weakReference = EasyMock.createStrictMock(WeakReference.class);
EasyMock.expect(weakReference.get()).andReturn(null);
EasyMock.replay(weakReference);
assertThat(weakReference.get(), nullValue());
EasyMock.verify(weakReference);
}
Works nicely, too.
Meaning: the generic answer to this problem is a factory that creates WeakReference for objects for you. So, when you want to test your production code; you provide a mocked factory to it; and that factory will in turn mocked WeakReference objects; and now you are in full control regarding the behavior of that weak reference object.
And "full control" is much better than assuming that the GC maybe does what you hope it is doing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With