javareferencegarbage-collectionsystemweak

Java weak reference is not freed after System.gc()


I've got a simple snippet:

Integer integer = 2;
WeakReference<Integer> wi = new WeakReference<>(integer);
WeakReference<Integer> sr = new WeakReference<>(new Integer(3));
System.out.println(wi.get());
System.out.println(sr.get());
System.gc();
System.out.println("step 1 wi = " + wi.get());
System.out.println("step 1 sr =: " + sr.get());
integer = null;
System.gc();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("step 1 wi = " + wi.get());
System.out.println("step 1 sr =: " + sr.get());

The "System.gc()" call should force all weak reference to be recycled, right, and I even waited 1 second to make sure gc() happens. But even when "integer = null" is set, "wi" refuse to be null. While sr is null right after "System.gc()". It prints:

2
3
step 1 wi = 2
step 1 sr =: null
step 1 wi = 2
step 1 sr =: null

My questions: (1) What is the core difference between wi and sr? (2 )how to make jvm recycle wi?

Thanks a lot.


Solution

  • It has nothing whatsoever to do with either the fact that System.gc() makes no guarantees (it doesn't, but that's not why you're seeing what you're seeing), or how weak references interact with gc.

    The j.l.Integer class holds a 'cache' of instances for Integer, for all byte values, so, from -128 to +127. You can watch this in action:

    System.out.println(Integer.valueOf(127) == Integer.valueOf(127));
    System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
    

    The above prints true false.

    This code:

    Integer x = 2;
    

    is syntax sugar. What the compiler actually ends up compiling, because 'autoboxing' is a figment of javac's imagination (the JVM knows nothing about it, it's entirely javac doing that for you), is this code:

    Integer x = Integer.valueOf(2);
    

    whereas, of course, if you call new Integer, you ALWAYS get a new object, because, well, that's what the spec says. You can test this too:

    System.out.println(new Integer(5) == new Integer(5));
    

    the above prints false.

    Effectively, the java.lang.Integer class itself holds a reference to the thing your wi reference points to, hence, it never gets collected.

    Retry the exact same code, but this time, instead of ' = 2', try ' = 128', and this time it'll collect same as sr will.