Netty内存池ByteBuf 内存回收
内存池ByteBuf 内存回收:
在前面的章节中我们有提到, 堆外内存是不受JVM 垃圾回收机制控制的, 所以我们分配一块堆外内存进行ByteBuf 操作时, 使用完毕要对对象进行回收, 本节就以PooledUnsafeDirectByteBuf 为例讲解有关内存分配的相关逻辑。PooledUnsafeDirectByteBuf 中内存释放的入口方法是其父类AbstractReferenceCountedByteBuf 中的release()方法:
- public boolean release() {
- return release0();
- }
- private boolean release0(int decrement) {
for (;;) {
int refCnt = this.refCnt;
if (refCnt < decrement) {
throw new IllegalReferenceCountException(refCnt, -decrement);
}- if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
if (refCnt == decrement) {
deallocate();
return true;
}
return false;
}
}
}
if (refCnt == decrement) 中判断当前byteBuf 是否没有被引用了, 如果没有被引用, 则通过deallocate()方法进行释放。因为我们是以PooledUnsafeDirectByteBuf 为例, 所以这里会调用其父类PooledByteBuf 的deallocate()方法:
- protected final void deallocate() {
- if (handle >= ) {
- final long handle = this.handle;
//表示当前的ByteBuf 不再指向任何一块内存- this.handle = -;
//这里将memory 也设置为null- memory = null;
//这一步是将ByteBuf 的内存进行释放- chunk.arena.free(chunk, handle, maxLength, cache);
//将对象放入的对象回收站, 循环利用- recycle();
- }
- }
跟进 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);
- }
- }
首先判断是不是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 - ) {
- 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) {
- // If it was not possible to cache the chunk, immediately recycle the entry
- 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 not need to be called while holding the synchronized lock.
- destroyChunk(chunk);
- }
- }
我们再跟到free()方法中:
- boolean free(PoolChunk<T> chunk, long handle) {
- chunk.free(handle);
- if (chunk.usage() < minUsage) {
- remove(chunk);
- // Move the PoolChunk down the PoolChunkList linked-list.
- return move0(chunk);
- }
- return true;
- }
chunk.free(handle)的意思是通过chunk 释放一段连续的内存,再跟到free()方法中:
- void free(long handle) {
- int memoryMapIdx = memoryMapIdx(handle);
- int bitmapIdx = bitmapIdx(handle);
- if (bitmapIdx != ) { // free a subpage
- PoolSubpage<T> subpage = subpages[subpageIdx(memoryMapIdx)];
- assert subpage != null && subpage.doNotDestroy;
- // Obtain the head of the PoolSubPage pool that is owned by the PoolArena and synchronize on it.
- // This is need as we may add it back and so alter the linked-list structure.
- 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 >= ) {
- final long handle = this.handle;
- //表示当前的ByteBuf 不再指向任何一块内存
- this.handle = -;
- //这里将memory 也设置为null
- memory = null;
- //这一步是将ByteBuf 的内存进行释放
- chunk.arena.free(chunk, handle, maxLength, cache);
- //将对象放入的对象回收站, 循环利用
- recycle();
- }
- }
最后, 通过recycle()将释放的ByteBuf 放入对象回收站,以上就是内存回收的大概逻辑。
Netty内存池ByteBuf 内存回收的更多相关文章
- netty源码解解析(4.0)-24 ByteBuf基于内存池的内存管理
io.netty.buffer.PooledByteBuf<T>使用内存池中的一块内存作为自己的数据内存,这个块内存是PoolChunk<T>的一部分.PooledByteBu ...
- 感悟优化——Netty对JDK缓冲区的内存池零拷贝改造
NIO中缓冲区是数据传输的基础,JDK通过ByteBuffer实现,Netty框架中并未采用JDK原生的ByteBuffer,而是构造了ByteBuf. ByteBuf对ByteBuffer做了大量的 ...
- Netty内存池及命中缓存的分配
内存池的内存规格: 在前面的源码分析过程中,关于内存规格大小我们应该还有些印象.其实在Netty 内存池中主要设置了四种规格大小的内存:tiny 是指0-512Byte 之间的规格大小,small 是 ...
- 支撑百万级并发,Netty如何实现高性能内存管理
Netty作为一款高性能网络应用程序框架,实现了一套高性能内存管理机制 通过学习其中的实现原理.算法.并发设计,有利于我们写出更优雅.更高性能的代码:当使用Netty时碰到内存方面的问题时,也可以更高 ...
- C++实现简单的内存池
多进程编程多用在并发服务器的编写上,当收到一个请求时,服务器新建一个进程处理请求,同时继续监听.为了提高响应速度,服务器采用进程池的方法,在初始化阶段创建一个进程池,池中有许多预创建的进程,当请求到达 ...
- 定长内存池之BOOST::pool
内存池可有效降低动态申请内存的次数,减少与内核态的交互,提升系统性能,减少内存碎片,增加内存空间使用率,避免内存泄漏的可能性,这么多的优点,没有理由不在系统中使用该技术. 内存池分类: 1. ...
- Linux编程之内存池的设计与实现(C++98)
假设服务器的硬件资源"充裕",那么提高服务器性能的一个很直接的方法就是空间换时间,即"浪费"服务器的硬件资源,以换取其运行效率.提升服务器性能的一个重要方法就是 ...
- Linux 内存池【转】
内存池(Memery Pool)技术是在真正使用内存之前,先申请分配一定数量的.大小相等(一般情况下)的内存块留作备用.当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存 ...
- nginx源代码分析之内存池实现原理
建议看本文档时结合nginx源代码. 1.1 什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理.应用程序申请内存时不再与OS打交道.而是从内存池中申请内存或者释放内存到内存池 ...
随机推荐
- win32 界面 背景图片
case WM_PAINT: { HBITMAP hbm; BITMAP bminfo; hbm = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE ...
- Facebook再现丑闻,约100位应用程序开发人员偷看用户数据
Facebook今天披露了另一起安全事件,承认大约100名应用程序开发人员可能不正确地访问了某些Facebook组中的用户数据,包括他们的姓名和个人资料图片. 在周二发布的博客文章中,Facebook ...
- wepy-开发总结(功能点)
开发小程序中,遇到的wepy的几点坑,记录一下; 更详细的项目总结记录请见我的个人博客:https://fanghongliang.github.io/ 1.定时器: 在页面中有需要用到倒计时或者其他 ...
- 如何用DNS+GeoIP+Nginx+Varnish做世界级的CDN
如何用DNS+GeoIP+Nginx+Varnish做世界级的CDN 如何用BIND, GeoIP, Nginx, Varnish来创建你自己的高效的CDN网络?CDN,意思是Content ...
- 利用雅虎ycsb对cassandra做性能测试
准备: 环境: 两台虚拟机:ip:192.168.138.128/129;配置:2核4G: 版本:apache-cassandra-3.10 ycsb-cassandra-binding-0.1 ...
- 【leetcode】1111. Maximum Nesting Depth of Two Valid Parentheses Strings
题目如下: A string is a valid parentheses string (denoted VPS) if and only if it consists of "(&quo ...
- font-size:0; 消除空白间隙
使用font-size:0解决设置inline-block引起的空白间隙问题 一.空白间隙问题 在进行页面布局的时候为了页面代码所谓整洁刻度,往往会设置缩进或是换行,但是元素display为inlin ...
- k8s登录harbor报错:Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request cance
[root@k8s-node02 ~]# docker login 192.168.180.105:1180 Username: admin Password: Error response from ...
- [BZOJ1934][SHOI2007]Vote 善意的投票:最小割
分析 先讲一下连边方法: \(S\)向意愿同意的人,意愿反对的人向\(T\),朋友之间互相连(其实好像意愿不同的朋友之间互相连就可以了,嘛,不管了),容量均为\(1\). 最小割即为答案. 可以理解为 ...
- 解决:父类中的@NotNull无效以及@Notnull 验证list对象无效
解决方法如图: controller层 vo.param层 父类验证注解要使用@NotEmpty 不能使用 @NotNull,否则验证无效的,反正笔者是没有成功过