A thread-local for
BufferPool
.
The BufferPoolThreadLocal is not assigned to a static field, but as a member field of the SerializationService-instance.
is unlike the common use of a ThreadLocal where it assigned to a static field. The reason behind this is that the BufferPool
instance belongs to a specific SerializationService instance (the buffer pool has buffers tied to a particular
SerializationService instance). By creating a ThreadLocal per SerializationService-instance, we obtain 'BufferPool per thread
per SerializationService-instance' semantics.
BufferPool is tied to SerializationService-instance
The BufferPool contains e.g.
BufferObjectDataOutput
instances that have a reference to a specific
SerializationService-instance. So as long as there is a strong reference to the BufferPool, there is a strong reference
to the SerializationService-instance.
The problem with regular ThreadLocals
In the Thread.threadLocal-map only the key (the ThreadLocal) is wrapped in a WeakReference. So when the ThreadLocal-instance
dies, the WeakReference prevents a strong reference to the ThreadLocal and potentially at some point in the future, the map
entry with the null valued WeakReference-key and the strong reference to the the value removed. The problem is we have
no control when this happens and we could end up with a memory leak because there are strong references to e.g. the
SerializationService.
BufferPoolThreadLocal wraps BufferPool also in WeakReference
To prevent this memory leak, the value (the BufferPool) in the Thread.threadLocals-map is also wrapped in a WeakReference
So if there is no strong reference to the BufferPool, the BufferPool is gc'ed. Even though there might by some empty key/value
WeakReferences in the Thread.threadLocal-map.
Strong references to the BufferPools
To prevent the BufferPool wrapped in a WeakReference to be gc'ed as soon as it is created, the BufferPool is also stored
in the BufferPoolThreadLocal in a ConcurrentReferenceHashMap with a weak reference for the key (the thread) and a
strong reference to the value (BufferPool):
This gives us the following behavior:
-
As soon as the Thread, having the ThreadLocal in its Thread.threadLocals-map, dies, the Entry with the Thread as key
will be removed from the ConcurrentReferenceHashMap at some point.
-
As soon as the SerializationService is collected, the BufferPoolThreadLocal is collected. And because of this, there
are no strong references to the BufferPool instances, and they get collected as well.
So it can be that a Thread in its Thread.threadLocal-map will for some time have an empty weak-reference as key and value. But
there won't be any references to the BufferPool/SerializationService.
Performance
The Performance of using a ThreadLocal in combination with a WeakReference is almost the same as using a ThreadLocal without
WeakReference. There is an extra pointer indirection and some additional pressure on the gc system since it needs to deal with
the WeakReferences, but the number of threads is limited.