目录
目标
概述
实战
创建直接内存的ByteBuf和堆内存的ByteBuf
创建池化的ByteBuf和非池化的ByteBuf
扩容ByteBuf
ByteBuf写出方法
ByteBuf读入方法
释放ByteBuf的内存
修改ByteBuf
对ByteBuf进行切片(逻辑上的切分)
复制ByteBuf(物理上的)
组合多个ByteBuf
池化的ByteBuf和非池化的ByteBuf的区别
类比数据库连接池,池化的ByteBuf可以被重用,对高并发有很好的节约内存的效果。4.1以前的版本默认创建非池化ByteBuf,4.1以后的版本默认创建池化的ByteBuf,但是Android默认创建非池化的ByteBuf。
直接内存的ByteBuf和堆内存的ByteBuf的区别
直接内存的ByteBuf创建和销毁代价大,但是读写性能高,因为少了一次内存复制。减少了垃圾回收机制的压力。
ByteBuf扩容规律
ByteBuf可以指定初始容量也可不指定。不指定初始容量,则容量默认256个字节大小。数据超过容量以后ByteBuf会自动扩容。扩容规则如下:
推荐创建ByteBuf的方法
在Pipeline中创建ByteBuf时推荐使用ChannelHandlerContext,如:
pipeline.addLast("InboundHandler2", new ChannelInboundHandlerAdapter() {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buffer = ctx.alloc().buffer(10);}});
/*** 创建基于直接内存和堆内存的ByteBuf* 直接内存创建和销毁代价大,但是读写速度快。*/public void directAndHeap(){//直接内存ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer();System.out.println(byteBuf.getClass());//堆内存ByteBuf byteBuf1 = ByteBufAllocator.DEFAULT.heapBuffer();System.out.println(byteBuf1.getClass());}
输出结果
第一步:以idea为例,需要做一下配置。
第二步
第三步:如图所示,需要将VM options设置为-Dio.netty.allocator.type=unpooled
第四步:创建ByteBuf并输出ByteBuf类名,此时发现ByteBuf变成了非池化类型。需要注意,ByteBuf默认创建池化类型。
/*** ByteBuf扩容案例* ByteBuf可以指定初始容量也可不指定。不指定初始容量,则容量默认256个字节大小。* 数据超过容量以后ByteBuf会自动扩容。扩容规则如下:* 扩容小于等于512,扩容为16的倍数大小。* 扩容大于512,扩容为2的n次方大小。*/public void addCapacity(){ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();System.out.println(buffer);StringBuffer sb = new StringBuffer();//添加257个字节大小的字符串。for(int i=0;i<257;i++){sb.append("a");}buffer.writeBytes(sb.toString().getBytes());System.out.println(buffer);}/*** ByteBuf扩容案例* ByteBuf可以指定初始容量也可不指定。不指定初始容量,则容量默认256个字节大小。* 数据超过容量以后ByteBuf会自动扩容。扩容规则如下:* 扩容小于等于512,扩容为16的倍数大小。* 扩容大于512,扩容为2的n次方大小。*/public void addCapacity2(){ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();System.out.println(buffer);StringBuffer sb = new StringBuffer();//添加513个字节大小的字符串。for(int i=0;i<513;i++){sb.append("a");}buffer.writeBytes(sb.toString().getBytes());System.out.println(buffer);}
public void writeTest() {ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();//写入byte[]byte[] bytes =new byte[]{'a', 'b', 'c'};byteBuf.writeBytes(bytes);//写入整数byteBuf.writeInt(1);//写入longbyteBuf.writeLong(1L);//写入字符串byteBuf.writeCharSequence("Hello World!",Charset.forName("UTF-8"));//写入StringBufferStringBuffer sb = new StringBuffer();byteBuf.writeCharSequence(sb,Charset.forName("UTF-8"));//写入java.nio.ByteBufferByteBuffer buffer = ByteBuffer.allocate(8);byteBuf.writeBytes(buffer);}
/*** 循环读取ByteBuf,读取过程中移动读指针。*/public void circulateReadTest(){ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();byteBuf.writeCharSequence("Hello World!",Charset.forName("UTF-8"));//循环读取ByteBuf,观察读指针(ridx)的变化情况。System.out.println(byteBuf);for(int i=byteBuf.readerIndex();i
概述
ByteBuf实现了io.netty.util.ReferenceCounted接口,采用了引用计数的方法来标记ByteBuf对象的使用情况。其中:
ByteBuf调用release()需要特别注意
第一:并不是在每个Handler的最后调用release(),而是最后一个使用ByteBuf的Handler负责计数减1。因为ByteBuf可能在多个Handler中传递,一旦每个Handler都负责计数减1,则下一个Handler就可能因为ByteBuf的底层内存被回收而无法使用。
第二:虽然Pipeline自带头部和尾部Handler,且会回收ByteBuf的内存,但前提是ByteBuf必须要流转至头部或者尾部,内存才会被回收。比如Handler_1将ByteBuf传递给Handler_2,Handler_2将ByteBuf转成String传递给头部和尾部Handler,则ByteBuf所在内存仍然没有被回收。
源码跟进
尾部Handler对对象计数减1的源码
io.netty.channel.ChannelPipeline接口
io.netty.channel.DefaultChannelPipeline类
io.netty.channel.DefaultChannelPipeline.TailContext类的channelRead(ChannelHandlerContext ctx, Object msg)方法
再跟进到了onUnhandledInboundMessage(Object msg)方法,该方法的内部有ReferenceCountUtil.release(msg)负责计数减1
进入release(msg),发现对象必须要实现io.netty.util.ReferenceCounted接口,对象计数才会被减1
public static boolean release(Object msg) {return msg instanceof ReferenceCounted ? ((ReferenceCounted)msg).release() : false;}
头部Handler对对象计数减1的源码
io.netty.channel.ChannelPipeline接口
io.netty.channel.DefaultChannelPipeline类
io.netty.channel.DefaultChannelPipeline.HeadContext类的write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)方法
再跟进到了io.netty.channel.AbstractChannel类的write(Object msg, ChannelPromise promise)方法,当没有出栈缓存时进入计数减1操作,同样地,进入release(msg)后发现对象必须要实现io.netty.util.ReferenceCounted接口,对象计数才会被减1
public final void write(Object msg, ChannelPromise promise) {this.assertEventLoop();ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;if (outboundBuffer == null) {try {ReferenceCountUtil.release(msg);} finally {this.safeSetFailure(promise, this.newClosedChannelException(AbstractChannel.this.initialCloseCause, "write(Object, ChannelPromise)"));}} else {int size;try {msg = AbstractChannel.this.filterOutboundMessage(msg);size = AbstractChannel.this.pipeline.estimatorHandle().size(msg);if (size < 0) {size = 0;}} catch (Throwable var15) {try {ReferenceCountUtil.release(msg);} finally {this.safeSetFailure(promise, var15);}return;}outboundBuffer.addMessage(msg, size, promise);}}public static boolean release(Object msg) {return msg instanceof ReferenceCounted ? ((ReferenceCounted)msg).release() : false;}
/*** 根据下标修改ByteBuf元素*/public void updateTest(){//创建一个初始容量为10字节大小的ByteBufByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(10);byteBuf.writeBytes(new byte[]{'1','2','3','4','5','6','7','8','9','0'});//修改下标为0的元素为'2'byteBuf.setByte(0,'2');for(int i=0;i
/*** 切分ByteBuf*/public void sliceTest(){//创建一个初始容量为10字节大小的ByteBufByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(10);byteBuf.writeBytes(new byte[]{'1','2','3','4','5','6','7','8','9','0'});//从下标2开始切分,切取5个长度。ByteBuf sliceByteBuf = byteBuf.slice(2, 5);for(int i=0;i
特别注意
推荐案例
public void sliceTest(){//创建一个初始容量为10字节大小的ByteBufByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(10);byteBuf.writeBytes(new byte[]{'1','2','3','4','5','6','7','8','9','0'});//从下标2开始切分,切取5个长度。ByteBuf sliceByteBuf = byteBuf.slice(2, 5);//引用计数加一。sliceByteBuf.retain();//将原始的ByteBuf释放掉。byteBuf.release();//发现原来的ByteBuf还可以继续使用。for(int i=0;i
/***copy()方法对ByteBuf进行了深拷贝,是物理上的复制,与原来的ByteBuf无关联。*/public void copyTest(){//创建一个初始容量为10字节大小的ByteBufByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(10);byteBuf.writeBytes(new byte[]{'1','2','3','4','5','6','7','8','9','0'});//复制整个原始的ByteBuf。ByteBuf cp =byteBuf.copy();cp.setByte(0,'b');for(int i=0;i
/*** 组合多个ByteBuf* 方法1:通过writeBytes方法组合多个ByteBuf。新组合的ByteBuf与之前的ByteBuf完全独立开。*/public void writeBytesTest(){ByteBuf byteBuf = ByteBufAllocator.DEFAULT.directBuffer(5);byteBuf.writeBytes(new byte[]{'1','2','3','4','5'});ByteBuf byteBuf2 = ByteBufAllocator.DEFAULT.directBuffer(5);byteBuf2.writeBytes(new byte[]{'6','7','8','9','0'});//ByteBuf byteBuf3 = ByteBufAllocator.DEFAULT.directBuffer(10);byteBuf3.writeBytes(byteBuf).writeBytes(byteBuf2);for(int i=0;i
上一篇:Input系统之APP建立联系
下一篇:让你少写多做的 ES6 技巧