From 010dbe3c734aff7b400c7be8666e5f0c055fca62 Mon Sep 17 00:00:00 2001 From: Robin Wang Date: Tue, 24 Apr 2018 15:19:01 +0800 Subject: [PATCH] Optimize space usage of FastThreadLocal (#7861) Motivation: A FastThreadLocal instance currently occupies 2 slots of InternalThreadLocalMap of every thread. Actually for a FastThreadLocalThread, it does not need to store cleaner flags of FastThreadLocal instances. Besides, using BitSet to store cleaner flags is also better for space usage. Modification: Use BitSet to optimize space usage of FastThreadLocal. Result: Avoid unnecessary slot occupancy. Cleaner flags are now stored into a BitSet. --- .../io/netty/util/concurrent/FastThreadLocal.java | 11 +++-------- .../util/internal/InternalThreadLocalMap.java | 14 ++++++++++++++ .../netty/util/concurrent/FastThreadLocalTest.java | 2 +- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java index e71d6db414..561058abca 100644 --- a/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java +++ b/common/src/main/java/io/netty/util/concurrent/FastThreadLocal.java @@ -125,11 +125,8 @@ public class FastThreadLocal { private final int index; - private final int cleanerFlagIndex; - public FastThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex(); - cleanerFlagIndex = InternalThreadLocalMap.nextVariableIndex(); } /** @@ -150,13 +147,11 @@ public class FastThreadLocal { private void registerCleaner(final InternalThreadLocalMap threadLocalMap) { Thread current = Thread.currentThread(); - if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || - threadLocalMap.indexedVariable(cleanerFlagIndex) != InternalThreadLocalMap.UNSET) { + if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || threadLocalMap.isCleanerFlagSet(index)) { return; } - // removeIndexedVariable(cleanerFlagIndex) isn't necessary because the finally cleanup is tied to the lifetime - // of the thread, and this Object will be discarded if the associated thread is GCed. - threadLocalMap.setIndexedVariable(cleanerFlagIndex, Boolean.TRUE); + + threadLocalMap.setCleanerFlag(index); // We will need to ensure we will trigger remove(InternalThreadLocalMap) so everything will be released // and FastThreadLocal.onRemoval(...) will be called. diff --git a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java index a0d186e74b..6ab3bdf131 100644 --- a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java @@ -26,6 +26,7 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.IdentityHashMap; import java.util.Map; import java.util.WeakHashMap; @@ -45,6 +46,8 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap public static final Object UNSET = new Object(); + private BitSet cleanerFlags; + static { STRING_BUILDER_INITIAL_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024); @@ -331,4 +334,15 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap Object[] lookup = indexedVariables; return index < lookup.length && lookup[index] != UNSET; } + + public boolean isCleanerFlagSet(int index) { + return cleanerFlags != null && cleanerFlags.get(index); + } + + public void setCleanerFlag(int index) { + if (cleanerFlags == null) { + cleanerFlags = new BitSet(); + } + cleanerFlags.set(index); + } } diff --git a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java b/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java index cc1e69fca7..4551d4f78b 100644 --- a/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java +++ b/common/src/test/java/io/netty/util/concurrent/FastThreadLocalTest.java @@ -47,7 +47,7 @@ public class FastThreadLocalTest { // Initialize a thread-local variable. assertThat(var.get(), is(nullValue())); - assertThat(FastThreadLocal.size(), is(2)); + assertThat(FastThreadLocal.size(), is(1)); // And then remove it. FastThreadLocal.removeAll();