Cost of using weak references in Java

Has anyone researched the runtime costs involved in creating and garbage collecting Java WeakReference objects? Are there any performance issues (e.g. contention) for multi-threaded applications?

EDIT: Obviously the actual answer(s) will be JVM dependent, but general observations are also welcome.

EDIT 2: If anyone has done some benchmarking of the performance, or can point to some benchmarking results, that would be ideal. (Sorry, but the bounty has expired ...)


Solution 1:

WeakReferences have negative impact on CMS garbage collector. As far as I can see from behavior of our server it influences parallel remark phase time. During this phase all app threads are stopped so it's extremely undesirable thing. So you need to be careful with WeakReferences.

Solution 2:

I implemented a Java garbage collector once, so whatever I was able to accomplish is a (weak :) lower bound on what is possible.

In my implementation, there is a small constant amount of additional overhead for each weak reference when it is visited during garbage collection.

So the upshot is: I wouldn't worry about it, it's not a big problem unless you are using zillions of weak references.

Most importantly, the cost is proportional to the number of weak references in existence, not the size of the overall heap.

However, that's not to say that a garbage collector that supports weak references will be as fast as one that does not. The presumed question here is, given that Java supports weak references, what is the incremental cost of using them?

Mine was a simple "stop the world" mark/sweep garbage collector. During garbage collection, it makes a determination for every object whether that object is live or not and sets a LIVE bit in the object header. Then it goes through and frees all the non-live objects.

To handle weak references you just add the following:

  • Ignore weak references when setting LIVE bits (i.e., they don't cause the LIVE bit on the referenced object to be set).
  • During the sweep step, add a special check as follows: if the object you're visiting is LIVE, and it's a WeakReference, then check the object that it weakly references, and if that object is not LIVE, clear the reference.

Small variations of this logic work for soft and phantom references.

Implementation is here if you're really curious.

Solution 3:

cache using weak references may significantly slow down your app if it's rebuild on demand e.g. in getters:

public Object getSomethingExpensiveToFind() {
    if(cache.contains(EXPENSIVE_OBJ_KEY)) {
        return cache.get(EXPENSIVE_OBJ_KEY);
    }

    Object sth = obtainSomethingExpensiveToFind(); // computationally expensive
    cache.put(EXPENSIVE_OBJ_KEY, sth);
    return sth;
} 

imagine this scenario:

1) app is running low on memory

2) GC cleans weak references, thus cache is cleared too

3) app continues, a lot of methods like getSomethingExpensiveToFind() are invoked and rebuild the cache

4) app is running low on memory again

5) GC cleans wear references, clears cache

6) app continues, a lot of methods like getSomethingExpensiveToFind() are invoked and rebuild the cache again

7) and so on...

I came upon such problem - the app was interrupted by GC very often and it compeletly defeated the whole point of caching.

In other words, if improperly managed, weak references can slow down your application.