Search code examples
javaspringconcurrencyhashmap

How does ConcurrentReferenceHashMap work?


We have a situation using spring-cloud-netflix-core library which is described here. I think the issue is with the way CachingSpringLoadBalancerFactory is using ConcurrentReferenceHashMap which is reported here.
And also from the documentation of ConcurrentReferenceHashMap (which uses soft references):

The use of references means that there is no guarantee that items placed into the map will be subsequently available. The garbage collector may discard references at any time, so it may appear that an unknown thread is silently removing entries.

Now my questions are:
1. Is my understanding in the following correct?

// Original code is in CachingSpringLoadBalancerFactory in spring-cloud-netflix-core
// cache is a field of type ConcurrentReferenceHashMap
if (this.cache.containsKey(clientName)) {
  return this.cache.get(clientName); // This can be null, right?
}

2. Is there anyway to write a test case for it. We managed to reproduce it once in an standalone application with limited memory (-Xmx50m). But how can we write a unit test to cover such scenario?


Solution

  • As to your question, yes, that's correct. Generally, this is a variation on WeakHashMap, as stated in the docs.

    The JavaDoc for WeakHashMap says:

    "The behavior of the WeakHashMap class depends in part upon the actions of the garbage collector, so several familiar (though not required) Map invariants do not hold for this class. Because the garbage collector may discard keys at any time, a WeakHashMap may behave as though an unknown thread is silently removing entries. In particular, even if you synchronize on a WeakHashMap instance and invoke none of its mutator methods, it is possible for the size method to return smaller values over time, for the isEmpty method to return false and then true, for the containsKey method to return true and later false for a given key, for the get method to return a value for a given key but later return null, for the put method to return null and the remove method to return false for a key that previously appeared to be in the map, and for successive examinations of the key set, the value collection, and the entry set to yield successively smaller numbers of elements."

    Because of this behavior, it is quite difficult to test. You can use the getReference() method of the map and the release() method of the corresponding elements to simulate the purges if you want to test some invariants.