前面我们介绍了PoolChunk以及针对page的更细粒度的PoolSubpage,其实在chunk的上层还有一个管理类:PoolChunkList,PoolChunkList负责管理多个chunk的生命周期,在此基础上对内存分配进行进一步的优化,那它是如何去做的呢?我们来简单的了解下,先看看它的几个属性:
PoolArena<T> arena; // 这个东西又出现了,看来是个终极boss啊,后面再提 // 没有list还有自己的next和prev节点,最终组成一个list的link list PoolChunkList<T> nextList; PoolChunkList<T> prevList; // chunk有prev和next两个属性,因此这里只用一个节点就可以维护一个chunk链 PoolChunk<T> head; // 当前list中的chunk最小使用比例 int minUsage; // 当前list中的chunk最大使用比例 int maxUsage;
上面第二行第三行出现了nextList,prevList,可以想到在netty内存分配(其实并不是netty发明的,这里只是代称,不要太纠结)里,除了chunk是被一个list管理的,连list本身也组成了一个更大的list。 为什么这么做了,先不急,我们先简单的看下PoolChunkList中的方法:
// 为buf分配指定大小的内存 boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) { // 如果list中没有chunk则直接返回,看来这个list本身没有创建chunk的能力啊,只是负责维护chunk链。 // PoolChunkList旁白:我不是内存的创建者,只是内存的搬运工 if (head == null) { return false; } // 我们一个一个chunk开始找 for (PoolChunk<T> cur = head;;) { long handle = cur.allocate(normCapacity); if (handle < 0) { // handle < 0表示分配失败,继续到下一个chunk尝试 cur = cur.next; if (cur == null) { return false; } } else { // 分配成功则将分配到的资源赋给ByteBuf cur.initBuf(buf, handle, reqCapacity); // 当前chunk的使用量超过一个上限阈值,则将其从当前list转移到下一个list if (cur.usage() >= maxUsage) { remove(cur); nextList.add(cur); } return true; } } }
这段代码有一处比较重要,当一个chunk的用量超过一定的比例,会将该chunk从当前list挪到下一个list中,这样挪有什么好处呢? 我们知道chunk本身是从连续的内存中分配一小段连续的内存,这样实际使用内存者读写很方便,然而这种策略也带来了一个坏处,随着内存的不断分配和回收,chunk中可能存在很多碎片。 碎片越来越多后我们想分配一段连续内存的失败几率就会提高。针对这种情况我们可以把使用比例较大的chunk放到更后面,而先从使用比例更小的chunk中更早,这样成功的几率就提高了。然而光把chunk往后放是不科学的,因为随着内存的释放,原先被严重瓜分的chunk中会存在越来越多的大块连续内存,所以还得在特定条件下把chunk从后往前调。调整的时机当然就是在内存释放的时候了:
// 释放指定chunk内的指定page或page内的subpage void free(PoolChunk<T> chunk, long handle) { // handle代表了chunk中的某个page chunk.free(handle); // 用量少于阈值则从当前list移到前一个list,如果不存在前一个list,则销毁chunk if (chunk.usage() < minUsage) { remove(chunk); if (prevList == null) { // 从这里我们可以看出在一个chunk经历了一些列的分配内存、释放内存之后,list会将整个chunk释放掉 // 这样如果在流量高峰期分配了较多内存,随着流量的慢慢回落,内存会慢慢的释放出来。 assert chunk.usage() == 0; arena.destroyChunk(chunk); } else { prevList.add(chunk); } } }
上面的代码可以看到,一个chunk最终是会被释放的,但释放的条件是当前chunk已经没有内存被使用了,所以我们必须保证在这个可以释放的PoolChunkList的minUsage=0,否则就会出现在使用中的chunk被回收的状况。
看完上面两个方法,我们会发现一个chunk的生命周期并不是在一个固定的list中的,随着内存的分配和释放,他也会进入到不同的list中去。这样我们就必须得注意,两个相邻的PoolChunkList,前一个list的maxUsage和后一个list的minUsage的值必须得有一段交叉得值来缓冲,否则会出现某个usage在临界值的chunk不停的在两个list之间来回移动。比如前一个list是【0,50】则后一个list可以是【25-75】而不能是【50-75】。同时也要注意尾节点上的maxUsage一定要等于100,这样chunk占满后才不会被继续往后挪(后面也没有可用list了)。
从前面的allocate方法可以看出,PoolChunkList本身没有chunk的创建,因此还需要给外部开放一个添加chunk的方法:
// 增加节点 void add(PoolChunk<T> chunk) { // 如果超过当前list的上限阈值,则放入下一个list if (chunk.usage() >= maxUsage) { nextList.add(chunk); return; } chunk.parent = this; if (head == null) { // 不存在头结点则该节点作为头结点 head = chunk; chunk.prev = null; chunk.next = null; } else { // 存在头结点则将该节点放到头结点之前,该节点成为头结点。 // 刚放入的节点使用比例相对更小,分配到资源的可能性更大,因此放到头结点 chunk.prev = null; chunk.next = head; head.prev = chunk; head = chunk; } }
移除方法(remove)比较简单就是从list中去掉该chunk,并改变相邻节点的指向,这里就不贴代码了。一个chunk被移除后有三种可能的去向:
1、由于使用量增大被添加到后面的list中;
2、由于使用量减小被添加到前面的list中;
3、由于所有内存被释放而直接被释放(destroy)。
先小小的总结下,PoolChunkList主要是为了提高内存分配的效率,每个list中包含多个chunk,而多个list又可以形成一个大的link list,在进行内存分配时,我们可以先从比较靠前的list中分配内存,这样分配到的几率更大。在高峰期申请过多的内存后,随着流量下降慢慢的释放掉多余内存,形成一个良性的循环。需要注意的时由于需要对无用的chunk进行释放,PoolChunkList形成的link list并不是一个完整的双向链表,而是一个包含出口的链表(这里说法可能不够准确,意思就是这个双向链表中的其中一个分头结点的节点只有一个next节点没有prev节点。
最后我们看看加入list后的样子:
相关推荐
赠送jar包:transport-netty4-client-5.5.1.jar; 赠送原API文档:transport-netty4-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty4-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...
赠送jar包:netty-transport-native-unix-common-4.1.73.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.73....
赠送jar包:netty-transport-native-unix-common-4.1.68.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.68.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.68....
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:netty-transport-native-unix-common-4.1.74.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.74....
赠送jar包:netty-transport-classes-epoll-4.1.74.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.74.Final-sources.jar;...
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
赠送jar包:reactor-netty-http-1.0.15.jar; 赠送原API文档:reactor-netty-http-1.0.15-javadoc.jar; 赠送源代码:reactor-netty-http-1.0.15-sources.jar; 赠送Maven依赖信息文件:reactor-netty-...
赠送jar包:transport-netty3-client-5.5.1.jar; 赠送原API文档:transport-netty3-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty3-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...
赠送jar包:netty-resolver-dns-4.1.65.Final.jar; 赠送原API文档:netty-resolver-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-resolver-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:netty-codec-mqtt-4.1.73.Final.jar; 赠送原API文档:netty-codec-mqtt-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-mqtt-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:reactor-netty-core-1.0.15.jar; 赠送原API文档:reactor-netty-core-1.0.15-javadoc.jar; 赠送源代码:reactor-netty-core-1.0.15-sources.jar; 赠送Maven依赖信息文件:reactor-netty-core-1.0.15....
赠送jar包:netty-transport-native-unix-common-4.1.73.Final.jar; 赠送原API文档:netty-transport-native-unix-common-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-native-unix-common-4.1.73....
赠送jar包:transport-netty3-client-5.5.1.jar; 赠送原API文档:transport-netty3-client-5.5.1-javadoc.jar; 赠送源代码:transport-netty3-client-5.5.1-sources.jar; 赠送Maven依赖信息文件:transport-netty...
netty-socketio-netty-socketio-2.0.6 ,Socket.IO 是一个库,可以在客户端和服务器之间实现低延迟, 双向和基于事件的通信:netty-socketio-netty-socketio-2.0.6.tar.gznetty-socketio-netty-socketio-2.0.6.zip
赠送jar包:netty-codec-haproxy-4.1.73.Final.jar; 赠送原API文档:netty-codec-haproxy-4.1.73.Final-javadoc.jar; 赠送源代码:netty-codec-haproxy-4.1.73.Final-sources.jar; 赠送Maven依赖信息文件:netty-...
赠送jar包:transport-netty4-client-6.3.0.jar; 赠送原API文档:transport-netty4-client-6.3.0-javadoc.jar; 赠送源代码:transport-netty4-client-6.3.0-sources.jar; 赠送Maven依赖信息文件:transport-netty...
赠送jar包:reactor-netty-http-1.0.11.jar; 赠送原API文档:reactor-netty-http-1.0.11-javadoc.jar; 赠送源代码:reactor-netty-http-1.0.11-sources.jar; 赠送Maven依赖信息文件:reactor-netty-...
赠送jar包:netty-codec-mqtt-4.1.74.Final.jar; 赠送原API文档:netty-codec-mqtt-4.1.74.Final-javadoc.jar; 赠送源代码:netty-codec-mqtt-4.1.74.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-...
赠送jar包:netty-codec-dns-4.1.65.Final.jar; 赠送原API文档:netty-codec-dns-4.1.65.Final-javadoc.jar; 赠送源代码:netty-codec-dns-4.1.65.Final-sources.jar; 赠送Maven依赖信息文件:netty-codec-dns-...