jemalloc的另一個重要的概念是本地緩沖Thread-Local Storage,將釋放后的內存使用信息保存在線程中以提高內存分配效率。
在Netty中,擔負TLS的類有:
* PoolThreadLocalCache 類似ThreadLocal對象,內部保存線程本地緩存
* PoolThreadCache 緩沖池,每個線程一個實例,保存回收的內存信息
* MemoryRegionCache 內部有一個隊列,保存了內存釋放時的數據Chunk和Handle
* Recycler 一個輕量級對象池,
## 8.2.1 PoolThreadLocalCache
PoolThreadLocalCache繼承FastThreadLocal對象,FastThreadLocal是netty自己實現的一直ThreadLocal機制,詳細實現可以參見ThreadLocal一節。我們可以將其當做一個普通的FastThreadLocal理解,每個線程保存了PoolThreadCache對象,使用get時,如果ThreadLocal內部沒有則會調用initialValue()方法創建。PoolThreadCache創建過程中會選擇內存使用最少的Arena來創建PoolThreadCache。
```
final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
@Override
protected synchronized PoolThreadCache initialValue() {
final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
return new PoolThreadCache(
heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
}
@Override
protected void onRemoval(PoolThreadCache threadCache) {
threadCache.free();
}
private <T> PoolArena<T> leastUsedArena(PoolArena<T>[] arenas) {
if (arenas == null || arenas.length == 0) {
return null;
}
PoolArena<T> minArena = arenas[0];
for (int i = 1; i < arenas.length; i++) {
PoolArena<T> arena = arenas[i];
if (arena.numThreadCaches.get() < minArena.numThreadCaches.get()) {
minArena = arena;
}
}
return minArena;
}
}
```
## 8.2.2 PoolThreadCache
PoolThreadCache記錄了線程本地保存的內存池,分配的ByteBuf釋放時會被保存到該對象的實例中。PoolThreadCache內部保存了tiny/small/normal的堆內存和直接內存的MemoryRegionCache數組
```
final PoolArena<byte[]> heapArena; // 堆Arena
final PoolArena<ByteBuffer> directArena; // 直接內存Arena
private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;// tiny-heap
private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;// small-heap
private final MemoryRegionCache<byte[]>[] normalHeapCaches;// normal-heap
private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;// tiny-direct
private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;// small-direct
private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;// normal-direct
```
數組的大小與Arena中tinySubpagePools和smallSubpagePools的大小一樣,NormalMemoryRegionCache繼承了MemoryRegionCache對象,內部的queue保存了chunk和handle,根據這兩個可以定位到chunk中對應的范圍。
## 8.2.3 Recycler
Recycler是一個基于ThreadLocal棧的輕量級的對象池,在實現上,線程內部的threadLocal保存Stack對象,Stack內部保存了Handler,
內部有一個Handle接口,recycle方法用來回收對象
```
public interface Handle<T> {
void recycle(T object);
}
```
在使用時,需要重寫Recycler的newObject方法,該方法會在get時使用,如果本地線程池沒有可重復使用的對象則調用newObject返回一個新對象。
```
//
public static Recycler<MObject> RECYCLER = new Recycler<MObject>() {
@Override
protected MObject newObject(Handle<MObject> handle) {
return new MObject(handle);
}
};
```
之后,我們就可以講對象的獲取交給RECYCLER處理
```
public static void main(String[] args) {
MObject obj1 = RECYCLER.get();// 獲取對象
System.out.println(obj1); // obj1 地址 1418370913
MObject obj2 = RECYCLER.get(); // 再次獲取
System.out.println(obj2); // obj2 地址 361993357
obj1.free();
obj2.free();
System.out.println(RECYCLER.get());// 地址1418370913,重用了obj1
System.out.println(RECYCLER.get());// 地址625576447 創建了新對象
}
```
Recycler的get方法,首先從本地獲取stack,如果為空會創建并保存到線程本地。之后從stack中獲取對象,如果存在則返回。注意stack中的對象是Handler對象,Handler的value才是newObject返回的對象。
```
public final T get() {
if (maxCapacityPerThread == 0) {
return newObject((Handle<T>) NOOP_HANDLE);
}
Stack<T> stack = threadLocal.get(); // 從本地stack中獲取 沒有則創建
DefaultHandle<T> handle = stack.pop(); // 獲取stack里面的handler
if (handle == null) { // 如果handler為空,則創建一個,
handle = stack.newHandle();
handle.value = newObject(handle); // 創建對象
}
return (T) handle.value;
}
```
釋放對象時,需要通過Handler的recycle方法完成,
```
public void recycle(Object object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
stack.push(this);
}
```