Promptly freeing the per-thread cached direct buffers when a thread exits

Tony Printezis tprintezis at
Fri Apr 6 14:14:26 UTC 2018

Was there a reason why this was not introduced in the first place?


Tony Printezis | @TonyPrintezis | tprintezis at

On April 6, 2018 at 8:49:17 AM, Peter Levart (peter.levart at wrote:

On 04/06/2018 10:02 AM, Alan Bateman wrote:
> On 05/04/2018 22:45, Tony Printezis wrote:
>> Hi all,
>> We recently hit another interesting issue with the NIO thread-local
>> DirectByteBuffer cache. One of our services seems to create and drop
>> threads at regular intervals. I assume this is due to a thread pool
>> dynamically resizing itself.
>> Let's say a thread starts, lives long enough for its Thread object to be
>> promoted to the old gen (along with its cached direct buffer), then
>> exits.
>> This will result in its cached direct buffer(s) being unreachable in the
>> old gen and will only be reclaimed after the next full GC /
>> concurrent GC
>> cycle.
> Right, if a short lived thread does I/O with a buffer backed by an
> array in the heap then any direct buffers in its thread local cache
> won't be freed until there is a GC and reference processing. It's
> something that I've prototyped a few times and always leaned towards
> not exposing anything in the API, instead just hooking into the thread
> exit to clear the buffer cache. One thing to watch out for is that the
> buffer cache may not exist (as the thread didn't do any I/O with heap
> buffers) so you'll end up creating (an empty) buffer cache at thread
> exit. That is benign of course but still unsettling to have additional
> code executing at this time.
> -Alan

An internal method, let's say ThreadLocal.probe(), that would return
thread-local value only if it has already been initialized, would be
helpfull. Maybe it could be exposed as new public API too. I remember
that I needed it in the past:

    public T probe() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                T result = (T)e.value;
                return result;
        return null;

Regards, Peter

More information about the core-libs-dev mailing list