看了前面的PoolArena分析,我们知道PoolArena在分配内存时,使用了synchronized来保证线程安全,这样就带来了一定的效率问题。如何能再此基础上再优化呢,答案很简单,使用ThreadLocal类似的解决方案避免加锁! 为什么可以使用ThreadLocal,如果了解过netty线程模型(有兴趣后续可以一起学习),应该知道netty的woker线程池,它负责了数据的接收、发送(编解码这里先不说),并且保证同一个连接在同一个线程中完成这两个步骤,因此很容易利用ThreadLocal进行内存的分配和回收优化。
下面我们来看看内存池内存分配流程:
1、ByteBufAllocator 准备申请一块内存;
2、尝试从PoolThreadCache中获取可用内存,如果成功则完成此次分配,否则继续往下走,注意后面的内存分配都会加锁;
3、如果是小块(可配置该值)内存分配,则尝试从PoolArena中缓存的PoolSubpage中获取内存,如果成功则完成此次分配;
4、如果是普通大小的内存分配,则从PoolChunkList中查找可用PoolChunk并进行内存分配,如果没有可用的PoolChunk则创建一个并加入到PoolChunkList中,完成此次内存分配;
5、如果是大块(大于一个chunk的大小)内存分配,则直接分配内存而不用内存池的方式;
6、内存使用完成后进行释放,释放的时候首先判断是否和分配的时候是同一个线程,如果是则尝试将其放入PoolThreadCache,这块内存将会在下一次同一个线程申请内存时使用,即前面的步骤2;
7、如果不是同一个线程,则回收至chunk中,此时chunk中的内存使用率会发生变化,可能导致该chunk在不同的PoolChunkList中移动,或者整个chunk回收(chunk在q000上,且其分配的所有内存被释放);同时如果释放的是小块内存(与步骤3中描述的内存相同),会尝试将小块内存前置到PoolArena中,这里操作成功了,步骤3的操作中才可能成功。
从前面的几个步骤,加上netty本身的线程模型,可以知道ThreadLocal被使用到的几率是很大的(本身的网络io都能用到)。
ThreadLocal管理的对象为PoolThreadCache,由于tiny\small\normal在内存分配上的不同(tiny和small使用subpage,normal使用page),PoolThreadCache中也分了tinySubPageHeapCaches、smallSubPageHeapCaches、normalSubPageHeapCaches三个数组,数据的使用方式与PoolArena中的subpagePools相同,不同的index放不同大小的相应对象,这里数组中对应的类为MemoryRegionCache,来看看它的几个重要属性:
private final Entry<T>[] entries; private final int maxUnusedCached; private int head; private int tail; private int maxEntriesInUse; private int entriesInUse; private static final class Entry<T> { PoolChunk<T> chunk; long handle; }
entries:存放可分配内存的数组,entry维护一个chunk的一段内存标识;
head:当前第一个可分配内存在entries中的位置;
tail:当前数组中可以放数据的第一个可用位置;
在初始化的时候head=tail=0, 随着cache中慢慢有数据加入,tail会不停的增加,当大于等于entries.length时回到0,形成一个环形数组。 同时每次往entries中放数据时,如果发现tail对应位置存在可用的chunk,则说明池已满,此时放弃加入cache。 随着不停分配内存的需求,head也会不停的增加,当其所在位置的entry为空时,说明cache中无可用内存,此时会进入上面的步骤3进行内存分配。
maxUnusedCached:entries.length/2
maxEntriesInUse: cache的最大同时使用数;
entriesInUse: cache中正在使用的内存数目;
如果cache的复用率不高,则会尝试对cahe中的数据进行收缩,每执行freeSweepAllocationThreshold次(可配置)内存分配进行一次收缩,逻辑如下:
1. free=cache中的可用内存段的数目 - 最大同时使用数,及在内存分配高峰期可能存在free个剩余的内存段;
2.如果free>maxUnusedCached,则说明cache中可用数量较多,而使用频率又比较低(maxEntriesInUse小),存在浪费的可能。此时从head开始释放掉free个内存段。
用过ThreadLocal的同学都知道,使用不当很容易导致内存泄露,而在netty中则使用了ThreadDeathWatcher来防止内存泄露。ThreadDeathWatcher的逻辑很简单,每1s判断指定的Thread是否alive,如果线程已经停止,则执行指定的逻辑:
ThreadDeathWatcher.watch(thread, freeTask); // 如果thread停止则执行freeTask private final Runnable freeTask = new Runnable() { @Override public void run() { // 释放所有持有的内存 free0(); } };
到此,netty内存池直接相关的代码已经分析完,其实你会发现netty就是实现了两个比较经典的分配策略,buddy allocation(见PoolChunk)和jemalloc(有一定改动,PooledByteBufAllocator+PoolArena+PoolChunk+PoolThreadCache),所以如果想了解更新的信息,可以按照上面两个关键词搜索。
相关推荐
赠送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包: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包: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包: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-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-...
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包: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包: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-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-...
赠送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-...