在找netty分配内存时
ByteBufAllocator.DEFAULT.directBuffer(4);
最终进入到PooledByteBufAllocator.newDirectBuffer,首先就找threadCache分配,这就是一个FastThreadLocal
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
PoolThreadCache cache = threadCache.get();
PoolArena<ByteBuffer> directArena = cache.directArena;
。。。
。。。
我们看看FastThreadLocal(简称ftl)和ThreadLocal(简称tl)区别在哪?为什么说ftl要配合ftlt(FastThreadLocalThread)使用性能才高于tl?
Threadlocal的具体解析可以参考threadlocal解析,这里大致说一下,threadLocal获取的值的时候先hash取模求出数组坐标i,然后获取entities[i],判断entities[i].key是不是自己,如果不是,就寻找i+1,循环。如果中间发现了entities[i].key==null,还要进行回收清理,将i到n (entities[n]==null,n是第一个为null的位置) 中间的元素重新hash,移动。效率一般,还存在内存泄漏。
我们看一下FastThreadLocal的实现。
构造函数:每一个fastThreadLocal都带有一个唯一的下标。这个下标是静态的生成器产生的,所以能保证全局唯一。
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
FastThreadLocal很多实现都是依靠InternalThreadLocalMap。InternalThreadLocalMap继承于UnpaddedInternalThreadLocalMap。UnpaddedInternalThreadLocalMap的主要属性。(Object[]数组直接装载用户的对象,不需要装载ftl的引用对象。因为根据ftl找对象是根据下标直接找,不会发生hash冲突,不需要再次比较key是否相等。)
// 这是一个ThreadLocal变量,用来兼容普通线程使用ftl
static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();
// 静态的坐标生成器
static final AtomicInteger nextIndex = new AtomicInteger();
// 数组 用来装实际对象的
Object[] indexedVariables;
FastThreadLocal的get方法。获取此线程的InternalThreadLocalMap然后再根据自身下标读取。
public final V get() {
return get(InternalThreadLocalMap.get());
}
public final V get(InternalThreadLocalMap threadLocalMap) {
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
FastThreadLocal的set方法。先获取此线程的InternalThreadLocalMap ,根据下标设置值。
// unsafe认为没有赋值
public final void set(V value) {
if (value != InternalThreadLocalMap.UNSET) {
set(InternalThreadLocalMap.get(), value);
} else {
remove();
}
}
public final void set(InternalThreadLocalMap threadLocalMap, V value) {
if (value != InternalThreadLocalMap.UNSET) {
if (threadLocalMap.setIndexedVariable(index, value)) {
addToVariablesToRemove(threadLocalMap, this);
}
} else {
remove(threadLocalMap);
}
}
了解一下addToVariablesToRemove方法。
private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
Set<FastThreadLocal<?>> variablesToRemove;
if (v == InternalThreadLocalMap.UNSET || v == null) {
variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
} else {
variablesToRemove = (Set<FastThreadLocal<?>>) v;
}
variablesToRemove.add(variable);
}
variablesToRemoveIndex是一个静态变量,等于0。再ftl的数组中,0号位置就是一个set,用来装着所有存入的ftl,协助removeAll。加入没有这个set,removeAll需要遍历整个数组,效率不高。有了这个set,只需要遍历整个set,根据每一个ftl的下标去清理数组就好了。
private static final int variablesToRemoveIndex =
InternalThreadLocalMap.nextVariableIndex();
InternalThreadLocalMap.get()。获取当前线程的InternalThreadLocalMap。ftl的get/set方法都需要先获取此线程的InternalThreadLocalMap。
public static InternalThreadLocalMap get() {
Thread thread = Thread.currentThread();
if (thread instanceof FastThreadLocalThread) {
return fastGet((FastThreadLocalThread) thread);
} else {
return slowGet();
}
}
获取当前线程的InternalThreadLocalMap分fast和slow,如果是ftlt就是fast获取。
看一下fast获取:ftlt本身内存有一个成员变量就是InternalThreadLocalMap。(初始化为32的长度数组)
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
}
如果是普通线程,slow获取 ,slowThreadLocalMap是一个tl变量,然后创建一个InternalThreadLocalMap赋值给slowThreadLocalMap。通过tl实现的
private static InternalThreadLocalMap slowGet() {
ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
InternalThreadLocalMap ret = slowThreadLocalMap.get();
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
总结一下:
- FastThreadLocal 通过分配下标直接定位 value ,不会有 hash 冲突,效率较高。
- FastThreadLocal 采用空间换时间的方式来提高效率。
- FastThreadLocal 需要配套 FastThreadLocalThread 使用,不然还不如原生 ThreadLocal。
- FastThreadLocal 使用最好配套 FastThreadLocalRunnable,这样执行完任务后会主动调用 removeAll 来移除所有 FastThreadLocal ,防止内存泄漏。
- FastThreadLocal 的使用也是推荐用完之后,主动调用 remove。