netty bytebuf内存释放,netty bytebuffer
目录
字节回收这里调用了发布0,跟进去我们首先分析自由的方法我们跟到躲藏中回到增加方法中我们回到自由的方法中前文传送门:ByteBuf使用子页面级别内存分配
ByteBuf回收
之前的章节我们提到过,堆外内存是不受虚拟机(Java虚拟机的缩写)垃圾回收机制控制的,所以我们分配一块堆外内存进行ByteBuf操作时,使用完毕要对对象进行回收,这一小节,就以PooledUnsafeDirectByteBuf为例讲解有关内存分配的相关逻辑
PooledUnsafeDirectByteBuf中内存释放的入口方法是其父类AbstractReferenceCountedByteBuf中的释放;排放;发布方法:
@ Override public boolean release(){ return release 0(1);}
这里调用了release0, 跟进去
私有布尔release 0(int decrement){ for(;){ int ref CNT=this。参考计数;if(ref CNT decrement){ throw new IllegalReferenceCountException(ref CNT,-decrement);} if(refcntupdater。compareandset(this,refCnt,ref CNT-decrement)){ if(ref CNT==decrement){ deallocate();返回真实}返回false} }}if(refCnt==decrement)中判断当前byteBuf是否没有被引用了,如果没有被引用,则通过解除分配()方法进行释放
因为我们是以PooledUnsafeDirectByteBuf为例,所以这里会调用其父类PooledByteBuf的重新分配方法:
protected final void deallocate(){ if(handle=0){ final long handle=this。手柄;这个。handle=-1;内存=空;chunk.arena.free(chunk、handle、maxLength、cache);recycle();}}this.handle=-1表示当前的ByteBuf不再指向任何一块内存
内存=空这里将记忆也设置为空
chunk.arena.free(chunk,handle,maxLength,cache)这一步是将ByteBuf的内存进行释放
回收()是将对象放入的对象回收站,循环利用
我们首先分析free方法
void free(PoolChunkT chunk,long handle,int normCapacity,PoolThreadCache cache) { //是否为未合并if(块。un pooled){ int size=chunk。区块大小();销毁大块(组块);activebyteshuge。add(-size);巨大的回收量。increment();} else { //那种级别的大小大小类大小类=大小类(定额容量);//加到缓存里如果(缓存!=null cache.add(this,chunk,handle,normCapacity,size class)){ return;} //将缓存对象标记为未使用freeChunk(chunk、handle、size类);}}
首先判断是不是unpooled, 我们这里是Pooled, 所以会走到else块中:
sizeClass(normCapacity)计算是哪种级别的size, 我们按照tiny级别进行分析
cache.add(this, chunk, handle, normCapacity, sizeClass)是将当前当前ByteBuf进行缓存
我们之前讲过, 再分配ByteBuf时首先在缓存上分配, 而这步, 就是将其缓存的过程, 跟进去:
boolean add(PoolArena<?> area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) { //拿到MemoryRegionCache节点 MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass); if (cache == null) { return false; } //将chunk, 和handle封装成实体加到queue里面 return cache.add(chunk, handle);}
首先根据根据类型拿到相关类型缓存节点, 这里会根据不同的内存规格去找不同的对象, 我们简单回顾一下, 每个缓存对象都包含一个queue, queue中每个节点是entry, 每一个entry中包含一个chunk和handle, 可以指向唯一的连续的内存
我们跟到cache中
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(); }}
假设我们是tiny类型, 这里就会走到cacheForTiny(area, normCapacity)方法中, 跟进去:
private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) { int idx = PoolArena.tinyIdx(normCapacity); if (area.isDirect()) { return cache(tinySubPageDirectCaches, idx); } return cache(tinySubPageHeapCaches, idx);}
这个方法我们之前剖析过, 就是根据大小找到第几个缓存中的第几个缓存, 拿到下标之后, 通过cache去超相对应的缓存对象:
private static <T> MemoryRegionCache<T> cache(MemoryRegionCache<T>[] cache, int idx) { if (cache == null idx > cache.length - 1) { return null; } return cache[idx];}
我们这里看到, 是直接通过下标拿的缓存对象
回到add方法中
boolean add(PoolArena<?> area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) { //拿到MemoryRegionCache节点 MemoryRegionCache<?> cache = cache(area, normCapacity, sizeClass); if (cache == null) { return false; } //将chunk, 和handle封装成实体加到queue里面 return cache.add(chunk, handle);}
这里的cache对象调用了一个add方法, 这个方法就是将chunk和handle封装成一个entry加到queue里面
我们跟到add方法中:
public final boolean add(PoolChunk<T> chunk, long handle) { Entry<T> entry = newEntry(chunk, handle); boolean queued = queue.offer(entry); if (!queued) { entry.recycle(); } return queued;}
我们之前介绍过, 从在缓存中分配的时候从queue弹出一个entry, 会放到一个对象池里面, 而这里Entry<T> entry = newEntry(chunk, handle)就是从对象池里去取一个entry对象, 然后将chunk和handle进行赋值
然后通过queue.offer(entry)加到queue中
我们回到free方法中
void free(PoolChunk<T> chunk, long handle, int normCapacity, PoolThreadCache cache) { //是否为unpooled if (chunk.unpooled) { int size = chunk.chunkSize(); destroyChunk(chunk); activeBytesHuge.add(-size); deallocationsHuge.increment(); } else { //那种级别的Size SizeClass sizeClass = sizeClass(normCapacity); //加到缓存里 if (cache != null && cache.add(this, chunk, handle, normCapacity, sizeClass)) { return; } freeChunk(chunk, handle, sizeClass); }}
这里加到缓存之后, 如果成功, 就会return, 如果不成功, 就会调用freeChunk(chunk, handle, sizeClass)方法, 这个方法的意义是, 将原先给ByteBuf分配的内存区段标记为未使用
跟进freeChunk简单分析下:
void freeChunk(PoolChunk<T> chunk, long handle, SizeClass sizeClass) { final boolean destroyChunk; synchronized (this) { switch (sizeClass) { case Normal: ++deallocationsNormal; break; case Small: ++deallocationsSmall; break; case Tiny: ++deallocationsTiny; break; default: throw new Error(); } destroyChunk = !chunk.parent.free(chunk, handle); } if (destroyChunk) { destroyChunk(chunk); }}
我们再跟到free方法中:
boolean free(PoolChunk<T> chunk, long handle) { chunk.free(handle); if (chunk.usage() < minUsage) { remove(chunk); return move0(chunk); } return true;}
chunk.free(handle)的意思是通过chunk释放一段连续的内存
再跟到free方法中:
void free(long handle) { int memoryMapIdx = memoryMapIdx(handle); int bitmapIdx = bitmapIdx(handle); if (bitmapIdx != 0) { PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)]; assert subpage != null && subpage.doNotDestroy; PoolSubpage<T> head = arena.findSubpagePoolHead(subpage.elemSize); synchronized (head) { if (subpage.free(head, bitmapIdx & 0x3FFFFFFF)) { return; } } } freeBytes += runLength(memoryMapIdx); setValue(memoryMapIdx, depth(memoryMapIdx)); updateParentsFree(memoryMapIdx);}
if(bitmapIdx != 0)这里判断是当前缓冲区分配的级别是Page还是Subpage, 如果是Subpage, 则会找到相关的Subpage将其位图标记为0
如果不是subpage, 这里通过分配内存的反向标记, 将该内存标记为未使用
这段逻辑可以读者自行分析, 如果之前分配相关的知识掌握扎实的话, 这里的逻辑也不是很难
回到PooledByteBuf的deallocate方法中:
protected final void deallocate() { if (handle >= 0) { final long handle = this.handle; this.handle = -1; memory = null; chunk.arena.free(chunk, handle, maxLength, cache); recycle(); }}
最后, 通过recycle()将释放的ByteBuf放入对象回收站, 有关对象回收站的知识, 会在以后的章节进行剖析
以上就是内存回收的大概逻辑,更多关于Netty分布式ByteBuf使用回收的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。