forked from yuanmabiji/netty
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Netty的ByteBuf相关笔记.txt
51 lines (51 loc) · 5.63 KB
/
Netty的ByteBuf相关笔记.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1,ByteBuf除了有readerIndex,writerIndex,capacity外,还有一个maxCapacity参数,这个当需要扩容时用到。
2,ByteBuf有read,wirte和set方法
3,ByteBuf如果是read方法读Int的话,则readerIndex往后移动4个字节;读long的话,则readerIndex往后移动8个字节;
4,ByteBuf如果是write方法读Int的话,则writerIndex往后移动4个字节;读long的话,则writerIndex往后移动8个字节;
其实write方法实质调用的也是set方法;
5,ByteBuf的mark和reset方法:mark方法实质是保存readerIndex和writerIndex两个指针,reset方法实质是恢复原来保存的readerIndex和writerIndex两个指针;
6,ByteBuf的readableBytes=writerIndex-readerIndex,writableBytes=capacity-writerIndex和maxWritableBytes=maxCapacity-writerIndex。
7,AbstractByteBuf是实现的骨架,抽象的_setXXX和_getXXX方法留给子类实现;
8,ByteBuf的分类:Pooled和Unpooled,Unsafe和非Unsafe,Heap和Direct
9,PooledUnsafeHeapByteBuf和PooledHeapByteBuf有什么区别:PooledUnsafeHeapByteBuf是通过unsafe操作,而PooledHeapByteBuf是直接通过堆数组返回。
10,Direct内存需要手工释放。
11,UnpooledHeapByteBuf的底层是一个数组,基于数组来操作;UnpooledDirectByteBuf的底层是一个ByteBuffer对象即对应一个DirectByteBuffer对象,由UnpooledByteBufAllocator.newDirectBuffer方法新建;
12,内存分配接口,ByteBufAllocator的接口方法只定义了heap还是direct两个维度;
13,AbstractByteBufAllocator抽象类的newHeapBuffer或newDirectBuffer方法创建的堆或直接内存,是pool还是unpool,由子类PooledByteBufAllocator或UnpooledByteBufAllocator实现。
14,ByteBufAllocator有两大子类:UnpooledByteBufAllocator或PooledByteBufAllocator
15,Netty的Unsafe或非Unsafe的ByteBuf,netty是自动判别的,如果底层有unsafe对象,那么就分配unsafeByteBuf对象等,具体可以查看UnpooledByteBufAllocator.newHeapBuffer方法,因为都是新建数组,具体有啥区别没看出来,不过好像从getByte方法能体现出区别?
通过getByte方法可以看到最终是通过UNSAFE的native方法来实现的,那为何还要创建array数组呢?
16,UnPooledByteBufAllocator和PooledByteBufAllocator都可以通过newHeapBuffer和newDirectBuffer两个方法来展开分析。
17,directByteBuf是基于memoryAddress的,所以要保存内存地址,比如getByte是通过memoryAddress+index方式来的
18,PooledUnsafeDirectByteBuf的常量RECYCLER里面有个handle对象,handle对象是负责对象的回收
19,netty的内存分配是以chunk为单位的
20,netty内存分配:首先需要先申请一个16M的chunk,然后再从16M里面取一段内存(比如1M),然后再把这1M连续内存分到我们的ByteBuf里面。
21,chunk的切分是以Page为单位进行切分的。一个16M的chunk可以切分为2048个page(8k),一个8k的page可以切分为16个512B的SubPage;
22,每一个handler都是指向唯一一段连续的内存。
23,tiny[32],small[4],normal[3]中的每一个节点相当于一个MemoryRegionCache,每一个MemoryRegionCache中都由一个queue,假如要分配一个1k的
ByteBuf,首先定位到small类型的数据结构的第二个节点即1k大小的节点,找到这个节点后再从内部维护的queue里面取出一个ByteBuf,这样的话就
直接进行内存拿到分配,不需要再去chunk里面找一段连续的内存。tiny和normal的逻辑也是类似的。
问题:不同规格的cache是如何分配或初始化的?
24,MemoryRegionCache是PoolThreadCache里面的一个抽象静态类
25,每个线程里面会维护各自的PoolThreadCache,然后每个poolthreadcache都会维护三种规格的caches(直接或堆);
26,命中缓存的分配流程是从PoolArena.allocate方法开始的
27,调用ctx.writeAndFlush时,写自定义msg对象时,首先MessageToByteEncoder.write方法,然后再调用allocateBuffer方法来分配一个UnpooledUnsafeDirectByteBuf直接内存,
最后再把这个创建的UnpooledUnsafeDirectByteBuf封装为SimpleLeakAwareByteBuf对象 TODO 【Question11】 既然是UnPooled的直接内存,为何flush后还要释放呢?释放是指计数减1还是将内存归还内存池呢?
28,调用ctx.writeAndFlush如果写的是HeapByteBuffer消息,此时会调用到AbstractNioChannel的filterOutboundMessage方法,最终调用到AbstractNioChannel.newDirectBuffer方法来新建一个直接内存,然后释放堆内存
29,【Question12】 28步骤新建的直接内存又是在哪里释放的呢 【Answer12】是在ChannelOutboundBuffer的remove方法中释放和归还
30, TODO 【Question13】AbstractNioByteChannel的内部类NioByteUnsafe的read方法中的byteBuf = allocHandle.allocate(allocator);默认分配的是
UnpooledUnsafeDirectByteBuf(自己的netty-demo又是PooledUnsafeDirectByteBuf,不同版本不一样么还是有设置?)
(为何这里不封装为SimpleLeakAwareByteBuf对象呢??),最终好像也会在ByteToMessageDecoder的channelRead方法的finally块中调用cumulation.release();进行释放(只不过不是ReferenceCountUtil.release(msg);,两者等效吗???),
且《Netty实战》也提到:编码器和解码器的结构很简单,消息被编码后解码后会自动通过ReferenceCountUtil.release(message)释放,如果不想释放消息
可以使用ReferenceCountUtil.retain(message),这将会使引用数量增加而没有消息发布,大多数时候不需要这么做。
为何《netty进阶之路》的P43页说请求消息没有释放或者没被即使释放导致的堆外内存泄露???回公司确认下是不是MessageRecvHandler是不是也有ReferceCountUtil.release的逻辑??
31,TODO 【Question14】为何非内存池的方式的内存也要按照内存池的方式进行释放内存呢?《Netty进阶之路》的P48页
32,TODO 【Question15】为何NettyRPC项目中的netty框架无论是请求消息还是响应消息默认分配的ByteBuf都是非池化的直接内存呢?不是说默认是池化的内存么?
33,TODO 【Question16】 ReferenceCountUtil.release(msg)和ByteBuf.release()有啥区别?
34,TODO 【Question17】 Dubbo或NettyRPC是如何共用一个channel的?
35,TODO 【Question18】 动手实践:如何模拟内存泄露导致OOM?《Netty进阶之路》说到请求消息需要人工手动释放?如果不人工释放,则需要继承SimpleInboundHandler或调用ctx.channelRead方法将
消息转发给TailContext来释放?
36,若要深入学习netty的内存池机制,可以先深入学习下jemalloc的原理,再通过调试Netty的内存池源码来深入
37,不管是哪种类型的ByteBuf释放内存时最终都会调用到AbstractReferenceCountedByteBuf.release0方法来进行内存释放?如果是池化内存,最终会调用到PooledByteBuf.deallocate方法
38,Netty的Pool有用到内存池,ThreadLocal和对象池(缓存ByteBuf对象)等技术
39,TODO 【Question20】在netty中如何动态增删handler?