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.
This commit is contained in:
Robin Wang 2018-04-24 15:19:01 +08:00 committed by Norman Maurer
parent 1b1f7677ac
commit 010dbe3c73
3 changed files with 18 additions and 9 deletions

View File

@ -125,11 +125,8 @@ public class FastThreadLocal<V> {
private final int index; private final int index;
private final int cleanerFlagIndex;
public FastThreadLocal() { public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex(); index = InternalThreadLocalMap.nextVariableIndex();
cleanerFlagIndex = InternalThreadLocalMap.nextVariableIndex();
} }
/** /**
@ -150,13 +147,11 @@ public class FastThreadLocal<V> {
private void registerCleaner(final InternalThreadLocalMap threadLocalMap) { private void registerCleaner(final InternalThreadLocalMap threadLocalMap) {
Thread current = Thread.currentThread(); Thread current = Thread.currentThread();
if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || if (FastThreadLocalThread.willCleanupFastThreadLocals(current) || threadLocalMap.isCleanerFlagSet(index)) {
threadLocalMap.indexedVariable(cleanerFlagIndex) != InternalThreadLocalMap.UNSET) {
return; 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.setCleanerFlag(index);
threadLocalMap.setIndexedVariable(cleanerFlagIndex, Boolean.TRUE);
// We will need to ensure we will trigger remove(InternalThreadLocalMap) so everything will be released // We will need to ensure we will trigger remove(InternalThreadLocalMap) so everything will be released
// and FastThreadLocal.onRemoval(...) will be called. // and FastThreadLocal.onRemoval(...) will be called.

View File

@ -26,6 +26,7 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@ -45,6 +46,8 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap
public static final Object UNSET = new Object(); public static final Object UNSET = new Object();
private BitSet cleanerFlags;
static { static {
STRING_BUILDER_INITIAL_SIZE = STRING_BUILDER_INITIAL_SIZE =
SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024); SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
@ -331,4 +334,15 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap
Object[] lookup = indexedVariables; Object[] lookup = indexedVariables;
return index < lookup.length && lookup[index] != UNSET; 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);
}
} }

View File

@ -47,7 +47,7 @@ public class FastThreadLocalTest {
// Initialize a thread-local variable. // Initialize a thread-local variable.
assertThat(var.get(), is(nullValue())); assertThat(var.get(), is(nullValue()));
assertThat(FastThreadLocal.size(), is(2)); assertThat(FastThreadLocal.size(), is(1));
// And then remove it. // And then remove it.
FastThreadLocal.removeAll(); FastThreadLocal.removeAll();