1つの概要
、メモリ割り当てプール網状機構を使用するメモリの割り当てと回復を最適化するためにByteBuf
割り当てられ、実際にはByteBuf
、時間をメモリプール網状技術の使用だけでなく、セル技術は、(本明細書中で、一時的にオブジェクトを使用するPoolThreadCache
オブジェクトと呼ばれるプール技術を割り当てられます)。我々は、網状事前に割り当てられたメモリプールは、メモリの大部分知っているし、次に実際のメモリの事前に割り当てられたチャンクに必要な様々なサイズ分割ByteBuf
プール割り当てのうち、使用ByteBuf
メモリのこのチャンクで構成されているがデータアクセス、ByteBuf
実際にこの大きなメモリにおけるそれらの開始位置を記録し、その大きさ(範囲)に割り当てられています。
使用しない場合PoolThreadCache
、新しい割り当ては、各時間ByteBuf
割り当てアルゴリズムの使用を必要とするメモリの予め割り当てられたチャンクを割り当てられ、効率が比較的低く、使用がPoolThreadCache
であってもよく、ByteBuf
完全に使用するリサイクル使用ByteBuf
記録PoolThreadCache
、次同じ大きさ(同じ範囲)を割り当てる必要がByteBuf
直接、割り当てることができる正確にPoolThreadCache
バッファがないByteBuf
が、解放されるByteBuf
その開始位置、アクセス情報の最大長さのチャンクが、それは言うPoolThreadCache
ている「一時的に」以下に記述されたオブジェクトプール技法は、PoolThreadCache
特定の区別をすることによって保存されなくなりましたByteBuf
関連情報を直接保存されるByteBuf
保存されたダイレクトコールの説明を容易にするためにByteBuf
。加えて、PoolThreadCache
スレッドローカル変数を使用することは、 - FastThreadLocal
さらに改良されたメモリ割り当て効率であることができます。
この記事は、によってネッティーを使用する方法に焦点を当てPoolThreadCache
たメモリプールの割り当て効率を提供します。
2クラスの紹介
2.1 PoolThreadCache
私たちは、最初のを見てPoolThreadCache
導入し、メインドメインPoolThreadCache
時間領域を次のように分類され、ネッティーに、小さな小さな、通常は何かを理解する必要があります。
- 小さな:サイズ<512
- 小:サイズ> = 512とサイズ<のpageSize
- ノーマル:サイズ> =のpageSizeと<= CHUNKSIZE
- 巨大:サイズ> CHUNKSIZE
PoolThreadCache
唯一の小さな、小さな、ノーマルタイプキャッシュByteBuf
の大きすぎない巨大なキャッシュByteBuf
を参照してください以下のPoolThreadCache
主なフィールドを:
//PoolThreadCache
//因为PoolThreadCache主要持有那些需要被释放的ByteBuf,但是不会
//进行ByteBuf的分配工作,所以这里的heapArena和directArena主要用来
//进行相关缓存的计数,可以自行看下PoolThreadCache的构造函数
final PoolArena<byte[]> heapArena;
final PoolArena<ByteBuffer> directArena;
// Hold the caches for the different size classes, which are tiny, small and normal.
//下面则分别为堆内存、直接内存对应的tiny、small和normal的缓存
//PoolThreadCache采用三个数组保存了每种尺寸的ByteBuf,
//因为每种类型的尺寸都是返回值,比如tiny是size<512,所以
//每种类型的尺寸由根据大小计算下标组成了其对应的数组
//数据中的每个元素为MemoryRegionCache类型,MemoryRegionCache
//其实是一个queue,比如Netty中最小的ByteBuf为16,则tiny缓存数组
//第一个元素MemoryRegionCache队列就保存所有被回收的大小为16的
//ByteBuf信息
private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
private final MemoryRegionCache<byte[]>[] normalHeapCaches;
private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;
上記の配列を理解するために、我々はキャッシュからオブジェクトを取得し、オブジェクト・キャッシュ操作に出て実行すると、アレイからのコードのMemoryRegionCacheを取得しようとしているの件名を見てみましょう。
//下面函数输入参数normCapacity都是规范为2的指数方的数
//PoolArena
static int tinyIdx(int normCapacity) {
//size<512的是tiny,在这范围内的2的指数次方和对应下标如下:
/*
16: 1
32: 2
64: 4
128: 8
256: 16
*/
//tiny缓存数组大小默认为32
return normCapacity >>> 4;
}
static int smallIdx(int normCapacity) {
//512<=size<pageSize的为small,pageSize按照默认8192算的话
//在这范围内的2的指数次方和对应下标如下:
/* 512: 0
1024: 1
2048: 2
4096: 3
*/
//small缓存数组需要根据pageSize计算,如果pageSize=8192,则为4
int tableIdx = 0;
int i = normCapacity >>> 10;
while (i != 0) {
i >>>= 1;
tableIdx ++;
}
return tableIdx;
}
private MemoryRegionCache<?> cacheForNormal(PoolArena<?> area, int normCapacity) {
if (area.isDirect()) {
//pageSize<=size<=chunkSize的为small,
//pageSize按照默认8192算的话
//在这范围内的2的指数次方和对应下标如下:
/*
8192: 0
16384: 1
32768: 2
65536: 3
131072: 4
262144: 5
524288: 6
1048576: 7
2097152: 8
4194304: 9
8388608: 10
16777216: 11
*/
//normal缓存数组大小需要根据pageSize和order计算,
//如果pageSize=8192,则为12
int idx = log2(normCapacity >> numShiftsNormalDirect);
return cache(normalDirectCaches, idx);
}
int idx = log2(normCapacity >> numShiftsNormalHeap);
return cache(normalHeapCaches, idx);
}
小さなバッファアレイのpageSize = 8192の下に小さい、小さい、通常のバッファ配列の添字の計算例上記上部及び小配列添字インデックスMemoryRegionCache
キューバッファByteBuf
次のように大小関係は、次のとおりです。
- 512:0
- 1024:1
- 2048:2
- 4096:3
れるMemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches
インデックス0上に配置されるByteBuf
キュー512のサイズ、キューByteBuf
の数のPooledByteBufAllocator.DEFAULT_SMALL_CACHE_SIZE
制御、512デフォルト。回復はByteBuf
、サイズ512のすべてがByteBuf
に置かれますsmallSubPageDirectCaches[0]
キュー。
記事ではネッティーソース-PooledByteBufAllocator静的変数の初期化、著者は説明PooledByteBufAllocator
変数が導入にキャッシュされていない静的変数の初期化を、ここではこれらの重要な変数のいくつかは以下のとおりです。
//PooledByteBufAllocator
// cache sizes
//下面这些上面提到过,PoolThreadCache中每种缓存
//队列的大小
DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
// 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
// 'Scalable memory allocation using jemalloc'
DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
"io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
// the number of threshold of allocations when cached entries will be freed up if not frequently used
//控制如果一个队列中大于该数值的ByteBuf都分配出去了,就清空
//该队列
DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
"io.netty.allocator.cacheTrimInterval", 8192);
//这个变量控制是否对所有类型的线程都启用PoolThreadCache,还是只对
//FastThreadLocalThread实例的线程启用PoolThreadCache
DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
"io.netty.allocator.useCacheForAllThreads", true);
DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
"io.netty.allocator.directMemoryCacheAlignment", 0);
PoolThreadCache
実際には、それが上にあるPooledByteBufAllocator
(記事PoolThreadCacheの著者を参照してくださいネッティーソース-FastThreadLocal原則)、および以下のように初期化された関連:
//PooledByteBufAllocator
final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
private final boolean useCacheForAllThreads;
PoolThreadLocalCache(boolean useCacheForAllThreads) {
this.useCacheForAllThreads = useCacheForAllThreads;
}
@Override
protected synchronized PoolThreadCache initialValue() {
//获取当前分配出去最小容量(也就是空闲容量最多的)head或
//direct Arena
final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
Thread current = Thread.currentThread();
//DEFAULT_USE_CACHE_FOR_ALL_THREADS控制的
//是对所有线程都启用PoolThreadCache,还是只对
//FastThreadLocalThread启用
if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
return new PoolThreadCache(
heapArena, directArena, tinyCacheSize, smallCacheSize, normalCacheSize,
DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
}
//不启用PoolThreadCache则所有类型的cache都为0
// No caching so just use 0 as sizes.
return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0, 0);
}
//这个是缓存释放的关键
@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;
}
}
PoolThreadCache
メインコンストラクタが小さい/小/ nomalアレイが比較的簡単で作成し、記載していません。
上記のonRemoval
キャッシュを空にするための鍵である、あなたは知っておく必要がありますFastThreadLocalz
記事の著者を参照してください。具体的には、原則をクリーンアップするために網状ソース-FastThreadLocal原則。
2.2 MemoryRegionCache
MemoryRegionCache
これは、同じサイズであるByteBuf
(これは真実ではないことに注意ByteBuf
が、回収ByteBuf
情報)キュー、小型/小型または通常のサイズに応じて、存在するSubPageMemoryRegionCache
とNormalMemoryRegionCache
2つの実装、NormalMemoryRegionCache
nomalに対応しByteBuf
、SubPageMemoryRegionCache
小型及び小さい相当します。MemoryRegionCache
重要なフィールドは次のよう:
//MemoryRegionCache
//该大小的ByteBuf队列容量
private final int size;
//队列
private final Queue<Entry<T>> queue;
/*
枚举,标识该队列的尺寸类型
enum SizeClass {
Tiny,
Small,
Normal
}
*/
private final SizeClass sizeClass;
MemoryRegionCache
実際の要素はに格納されているMemoryRegionCache.Entry
タイプ、
//MemoryRegionCache.Entry
static final class Entry<T> {
final Handle<Entry<?>> recyclerHandle;
//可见Entry只保存了正在回收ByteBuf在chunk中的起始位置
//和对应的chunk
PoolChunk<T> chunk;
long handle = -1;
Entry(Handle<Entry<?>> recyclerHandle) {
this.recyclerHandle = recyclerHandle;
}
void recycle() {
chunk = null;
handle = -1;
recyclerHandle.recycle(this);
}
}
MemoryRegionCache.Entry
recyclerHandle
メインプール技術は、目的を達成するために、著者は、特定の記事見つけネッティーソースを-オブジェクトプールリサイクラーを。
三時無キャッシュByteBuf
の割り当て
比較するためにPoolThreadCache
、我々は見ていない場合は、配信を高速化する方法でPoolThreadCache
配分する方法でByteBuf
あり、ここでは単純に見PooledByteBufAllocator
キャッシュはのは直接メモリがないときに割り当てられていますか:
//PooledByteBufAllocator
@Override
protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
//没有启用缓存,只是PoolThreadCache队列尺寸为0,
//但是还是可以使用PoolThreadCache中的arena
//这里就是得到PoolThreadCache中的Arena
PoolThreadCache cache = threadCache.get();
PoolArena<ByteBuffer> directArena = cache.directArena;
final ByteBuf buf;
if (directArena != null) {
//进行分配
buf = directArena.allocate(cache, initialCapacity, maxCapacity);
} else {
buf = PlatformDependent.hasUnsafe() ?
UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
}
return toLeakAwareBuffer(buf);
}
//PoolArena
PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
//新建一个ByteBuf对象,这里使用的对象池技术
PooledByteBuf<T> buf = newByteBuf(maxCapacity);
//进行分配
allocate(cache, buf, reqCapacity);
return buf;
}
//下面根据此次分配内存所属尺寸分类tiny/small/normal,先尝试从
//PoolThreadCache中分配,这里因为没有启用缓存,所以尝试会失败,
//尝试失败之后,会新建PoolChunk(如果需要的话),然后采用结合平衡
//二叉树实现的内存分配算法进行运算,得出该ByteBuf在chunk大内存
//中的起始位置,最后分配完毕
private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
final int normCapacity = normalizeCapacity(reqCapacity);
if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
int tableIdx;
PoolSubpage<T>[] table;
boolean tiny = isTiny(normCapacity);
if (tiny) { // < 512
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = tinyIdx(normCapacity);
table = tinySubpagePools;
} else {
if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = smallIdx(normCapacity);
table = smallSubpagePools;
}
final PoolSubpage<T> head = table[tableIdx];
...
synchronized (this) {
allocateNormal(buf, reqCapacity, normCapacity);
}
incTinySmallAllocation(tiny);
return;
}
if (normCapacity <= chunkSize) {
if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
synchronized (this) {
allocateNormal(buf, reqCapacity, normCapacity);
++allocationsNormal;
}
} else {
// Huge allocations are never served via the cache so just call allocateHuge
allocateHuge(buf, reqCapacity);
}
}
見て有効でない場合PoolThreadCache
、分布ByteBuf
によれば、必要なメモリ割り当てアルゴリズムをPoolChunk
対応サイズの割り当てByteBuf
。
4 PoolThreadCache
キャッシュは、参加配布、クリーンアップ
以下は、キャッシュの回復や分布、によってデフォルトのpageSize = 8192、チャンク= pageSizeを>> 11を説明します。
上記のセクション2、関連するクラスを記述した後、キャッシュ・リカバリおよび分布は、を見てみましょう比較的簡単でByteBuf
配置する方法PoolThreadCache
インチ
キャッシュにオブジェクトをどのように4.1
最初のを見て、キャッシュにオブジェクトを理解するためにPoolChunk
実際に割り当てられたメモリの場所後の結果、初期化する方法ですByteBuf
。
//PoolChunk
void initBuf(PooledByteBuf<T> buf, long handle, int reqCapacity) {
int memoryMapIdx = memoryMapIdx(handle);
int bitmapIdx = bitmapIdx(handle);
if (bitmapIdx == 0) {
byte val = value(memoryMapIdx);
assert val == unusable : String.valueOf(val);
//可见,在调用ByteBuf的init方法时
//使用arena.parent.threadCache()传入了
//PoolThreadCache,也就是ByteBuf持有
//PoolThreadCache对象实例
buf.init(this, handle, runOffset(memoryMapIdx) + offset, reqCapacity, runLength(memoryMapIdx),
arena.parent.threadCache());
} else {
initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
}
}
私たちは、ネッティーに、ということを知っているByteBuf
参照カウント回復制御を使用して、カウントが0である、呼び出しdeallocate
方法はPooledByteBuf.deallocate
次のとおりです。
//PooledByteBuf
@Override
protected final void deallocate() {
if (handle >= 0) {
final long handle = this.handle;
this.handle = -1;
memory = null;
tmpNioBuf = null;
chunk.arena.free(chunk, handle, maxLength, cache);
chunk = null;
recycle();
}
}
//PoolArena
void free(PoolChunk<T> chunk, long handle, int normCapacity, PoolThreadCache cache) {
//非池化不用分配
if (chunk.unpooled) {
int size = chunk.chunkSize();
destroyChunk(chunk);
activeBytesHuge.add(-size);
deallocationsHuge.increment();
} else {
SizeClass sizeClass = sizeClass(normCapacity);
//加入缓存中
if (cache != null && cache.add(this, chunk, handle, normCapacity, sizeClass)) {
// cached so not free it.
return;
}
freeChunk(chunk, handle, sizeClass);
}
}
//PoolThreadCache
@SuppressWarnings({ "unchecked", "rawtypes" })
boolean add(PoolArena<?> area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) {
//根据回收的ByteBuf大小,从指定数组中获取对应队列下标处的
//MemoryRegionCache
MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass);
//如果为空,表示没有启用缓存,直接返回
if (cache == null) {
return false;
}
//如果启用缓存,则加入缓存中
return cache.add(chunk, handle);
}
//根据ByteBuf大小,从指定数组中获取队列,下标计算上面已经介绍过
//这里不再展开介绍
private MemoryRegionCache<?> cache(PoolArena<?> area, int normCapacity, SizeClass sizeClass) {
switch (sizeClass) {
case Normal:
return cacheForNormal(area, normCapacity);
case Small:
return cacheForSmall(area, normCapacity);
case Tiny:
return cacheForTiny(area, normCapacity);
default:
throw new Error();
}
}
//MemoryRegionCache
//将元素加入到MemoryRegionCache队列中
@SuppressWarnings("unchecked")
public final boolean add(PoolChunk<T> chunk, long handle) {
Entry<T> entry = newEntry(chunk, handle);
//这里offer考虑的队列的尺寸,可见第二节的介绍
boolean queued = queue.offer(entry);
//超出队列最大尺寸,则回收entry
if (!queued) {
// If it was not possible to cache the chunk, immediately recycle the entry
entry.recycle();
}
return queued;
}
キャッシュから4.2割り当て
キャッシュから調剤することは上記で概説した特定の場合に依存してきたPoolArena.allocate
方法:
//PoolArena
private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
final int normCapacity = normalizeCapacity(reqCapacity);
if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
int tableIdx;
PoolSubpage<T>[] table;
boolean tiny = isTiny(normCapacity);
if (tiny) { // < 512
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = tinyIdx(normCapacity);
table = tinySubpagePools;
} else {
if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
tableIdx = smallIdx(normCapacity);
table = smallSubpagePools;
}
final PoolSubpage<T> head = table[tableIdx];
/**
* Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
* {@link PoolChunk#free(long)} may modify the doubly linked list as well.
*/
synchronized (head) {
final PoolSubpage<T> s = head.next;
if (s != head) {
assert s.doNotDestroy && s.elemSize == normCapacity;
long handle = s.allocate();
assert handle >= 0;
s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
incTinySmallAllocation(tiny);
return;
}
}
synchronized (this) {
allocateNormal(buf, reqCapacity, normCapacity);
}
incTinySmallAllocation(tiny);
return;
}
if (normCapacity <= chunkSize) {
if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
}
synchronized (this) {
allocateNormal(buf, reqCapacity, normCapacity);
++allocationsNormal;
}
} else {
// Huge allocations are never served via the cache so just call allocateHuge
allocateHuge(buf, reqCapacity);
}
}
上記のコードは非常に長いですが、ロジックが複雑ではありません、我々は、特にキャッシュの超小型サイズから割り当てるする方法を見てByteBuf
上記のcache.allocateTiny
メソッド呼び出し:
//PoolThreadCache
boolean allocateTiny(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
//cacheForTiny根据所需ByteBuf大小从tiny缓存数据获取队列
return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
}
//根据所需ByteBuf大小从tiny缓存数据获取队列,这里数据结构和下标
//的运算在第2节介绍过
private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) {
int idx = PoolArena.tinyIdx(normCapacity);
if (area.isDirect()) {
return cache(tinySubPageDirectCaches, idx);
}
return cache(tinySubPageHeapCaches, idx);
}
//从队列中取出元素进行ByteBuf初始化
@SuppressWarnings({ "unchecked", "rawtypes" })
private boolean allocate(MemoryRegionCache<?> cache, PooledByteBuf buf, int reqCapacity) {
if (cache == null) {
// no cache found so just return false here
return false;
}
boolean allocated = cache.allocate(buf, reqCapacity);
//统计allocations,如果已经分配出去的ByteBuf大于
//freeSweepAllocationThreshold,即DEFAULT_CACHE_TRIM_INTERVAL
//配置的数值,则PoolThreadCache维护了allocations 记录了
//整个PoolThreadCache分配的总个数,每个MemoryRegionCache
//也有一个allocations记录了自己分配的个数,如果达到了清理
//条件,则清理队列中size-allocations个ByteBuf
//因为队列已经分配出去了allocations个元素,剩下的元素个数
//就是size-allocations,所以这里就是清空队列
if (++ allocations >= freeSweepAllocationThreshold) {
allocations = 0;
trim();
}
return allocated;
}
//MemoryRegionCache
public final boolean allocate(PooledByteBuf<T> buf, int reqCapacity) {
//从队列中取出一个ByteBuf信息,对待分配的ByteBuf进行初始化
//并将对应的entry放入对象池中
Entry<T> entry = queue.poll();
if (entry == null) {
return false;
}
initBuf(entry.chunk, entry.handle, buf, reqCapacity);
entry.recycle();
// allocations is not thread-safe which is fine as this is only called from the same thread all time.
++ allocations;
return true;
}
4.3 PoolThreaCache
クリーンアップ
上記導入されたPoolThreadCache
配置されPooledByteBufAllocator.FastThreadLocal
に、そして定義onRemoval
(記事の著者を参照して、アクティブおよびパッシブのクリーンアップにFTL FTLアクティブまたはパッシブと呼ばれるクリーンアップするメソッド網状源-FastThreadLocal原理)、onRemoval
方法呼び出すthreadCache.free()
メソッドを:
//PoolThreadCache
void free() {
//清空所有的缓存数据,具体的不再展开
int numFreed = free(tinySubPageDirectCaches) +
free(smallSubPageDirectCaches) +
free(normalDirectCaches) +
free(tinySubPageHeapCaches) +
free(smallSubPageHeapCaches) +
free(normalHeapCaches);
if (numFreed > 0 && logger.isDebugEnabled()) {
logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed, Thread.currentThread().getName());
}
//一些统计信息的变更
if (directArena != null) {
directArena.numThreadCaches.getAndDecrement();
}
if (heapArena != null) {
heapArena.numThreadCaches.getAndDecrement();
}
}