netty byteBuf使用

3 篇文章 0 订阅

package com.wang.wzrtunetty;

import org.apache.cassandra.cli.CliParser.newColumnFamily_return;

import com.wang.util.ByteUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufAllocatorMetricProvider;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;

public class NettyBuf {

    public void testHeapByteBuf() {
        ByteBuf heapBuf = Unpooled.buffer(10);  //io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf
        System.out.println("heapBuf is "+heapBuf.getClass().getName());
        heapBuf.writeByte(0x02);
        if (heapBuf.hasArray()) {
            byte[] array = heapBuf.array();
            int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
            int length = heapBuf.readableBytes();
            int writerIndex = heapBuf.writerIndex();
            int writeLength = heapBuf.writableBytes();
            //0,0        
            System.out.println("offset:"+offset+" length="+length);
            System.out.println("array:"+ByteUtil.toString(array));
            System.out.println("writerIndex:"+writerIndex);
            System.out.println("writeLength:"+writeLength);
        }
    }
    
    public void testDirectBuf(){
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();   //io.netty.buffer.PooledUnsafeDirectByteBuf
        System.out.println("byteBuf is "+byteBuf.getClass().getName());
        byteBuf.writeByte(0x01);
        byteBuf.writeByte(0x02);
        if(!byteBuf.hasArray()){  //heap有这个操作 direct的就没有这个操作了
//            byte[] array = byteBuf.array(); direct buffer 不能有这个操作
//            byteBuf.arrayOffset();//direct buffer 也没有这个操作
            System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex()+" "
                    +" byteBuf.writerIndex():"+byteBuf.writerIndex());
            int length = byteBuf.readableBytes();
            byte[] array = new byte[length];
            ByteBuf byteBuf2 = byteBuf.readBytes(array);
            System.out.println("direct array:"+ByteUtil.toString(array));
            System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex()+" "
                    +" byteBuf.writerIndex():"+byteBuf.writerIndex());
        }
        
        //ByteBuf 与 ByteBuffer的不同之处是,
        //ByteBuffer写完数据之后,需要 flip()才能读数据 因为 writeIndex和readIndex是同一个index
        //ByteBuf写完数据之后,不用flip(),可以直接读数据,因为writeIndex和readIndex不是同一个index
        //HeapBuf 有heapBuf.hasArray() byteBuf.arrayOffset() byteBuf.array()功能,而DirectBuf没有这些功能
    }
    
    
    public void readAndWrite(){
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
        System.out.println("byteBuf.getByte(1)="+ByteUtil.toString(new byte[]{byteBuf.getByte(3)}));
        System.out.println("byteBuf.capacity():"+byteBuf.capacity());
        System.out.println("byteBuf.writableBytes()="+byteBuf.writableBytes());//起始都是256 capacity
        byte[] bytes1 = ByteUtil.hexStringToBytes("0102030405060708");
        byteBuf.writeBytes(bytes1); //写了8个 capacity而没有变,但是写的能力却少了8个 ,而写的index也到了8个了,还没有读 readindex=0 读的容量8个,因为只写了8个
        System.out.println("byteBuf.capacity():"+byteBuf.capacity());
        System.out.println("byteBuf.writableBytes()="+byteBuf.writableBytes());
        System.out.println("byteBuf.writerIndex()="+byteBuf.writerIndex());
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        int length = byteBuf.readableBytes();
//        byte[] bytes2 = new byte[length];
        byte[] bytes2 = new byte[2];
        byteBuf.readBytes(bytes2); //读了2个,因此 index=2 ,read的容量少了2个=6
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readableBytes()="+byteBuf.readableBytes());
        System.out.println("bytes2="+ByteUtil.toString(bytes2));
        
        System.out.println("byteBuf.getByte(5)="+ByteUtil.toString(new byte[]{byteBuf.getByte(5)}));
        byteBuf.getByte(6);//使用getByte读数据,不会改变readIndex的,原来readIndex是几,还是几,对byteBuf没有影响
        System.out.println("getByte byteBuf.readerIndex()="+byteBuf.readerIndex());
        byteBuf.setByte(4, 0x13); //使用set也不会改变writeIndex,原来writeIndex是几,还是几,对byteBuf的影响就是那个位置的数,而这个也位置跟读没关系
        length = byteBuf.readableBytes();
        bytes2 = new byte[4];
        byteBuf.readBytes(bytes2);
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readableBytes()="+byteBuf.readableBytes());
        System.out.println("bytes2="+ByteUtil.toString(bytes2));
        
        byteBuf.clear(); //情况byteBuf readindex 和 writeIndex都是0了,从头了,
//        但是值还是有的,似乎值没有清空,可以通过byteBuf.getByte(7) byteBuf.getByte(3)看出来
        System.out.println("clearclearclearclearclear");
        System.out.println("byteBuf.getByte(3)="+ByteUtil.toString(new byte[]{byteBuf.getByte(3)}));
        System.out.println("byteBuf.getByte(7)="+ByteUtil.toString(new byte[]{byteBuf.getByte(7)}));         
        System.out.println("byteBuf.capacity():"+byteBuf.capacity());
        System.out.println("byteBuf.writerIndex()="+byteBuf.writerIndex());
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readableBytes()="+byteBuf.readableBytes());
        byte[] bytes3 = ByteUtil.hexStringToBytes("0a0b0c0d0e0f");
        byteBuf.writeBytes(bytes3);
        System.out.println("byteBuf.writeBytes(bytes3);byteBuf.writeBytes(bytes3);byteBuf.writeBytes(bytes3);");
        System.out.println("byteBuf.writerIndex()="+byteBuf.writerIndex());
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readerIndex()="+byteBuf.readerIndex());
        System.out.println("byteBuf.readableBytes()="+byteBuf.readableBytes());
        System.out.println("byteBuf.getByte(7)="+ByteUtil.toString(new byte[]{byteBuf.getByte(7)}));
        length = byteBuf.readableBytes();
        byte[] bytes4 = new byte[length];
        byteBuf.readBytes(bytes4);
        System.out.println("bytes4="+ByteUtil.toString(bytes4));
//        ReferenceCountUtil.refCnt(byteBuf);
        ReferenceCountUtil.release(byteBuf);//释放byteBuf,在通过ByteBuf.get()就会报错了
        System.out.println("ReferenceCountUtil.releaseReferenceCountUtil.releaseReferenceCountUtil.release");
//        System.out.println("byteBuf.getByte(3)="+ByteUtil.toString(new byte[]{byteBuf.getByte(3)}));
        
        byteBuf = ByteBufAllocator.DEFAULT.buffer();  //看来这种也不能释放
        System.out.println("byteBuf.getByte(1)="+ByteUtil.toString(new byte[]{byteBuf.getByte(1)}));
        
    }
    
    
    public static void main(String[] args) {
        NettyBuf nettyBuf = new NettyBuf();
//        nettyBuf.testHeapByteBuf();
//        nettyBuf.testDirectBuf();
        nettyBuf.readAndWrite();
    }
}

最基本的规则是谁最后访问ByteBuf,谁最后负责释放。需注意的是:

(1)发送组件将ByteBuf传递给接收组件,发送组件一般不负责释放,由接收组件释放;

(2)如果一个组件除了接收处理ByteBUf,而不做其他操作(比如再传给其他组件),这个组件负责释放ByteBuf。

在 ChannelHandler负责链中,如何释放

(1)在Inbound messages中

a. 如果ChannelHandler中,只有处理ByteBuf的操作,不会调ctx.fireChannelRead(buf)把ByteBuf传递下去,那就要在这个ChannelHandler中释放ByteBuf。

b. 如果ChannelHandler中,会调ctx.fireChannelRead(buf)把ByteBuf传递给下一个ChannelHandler,那在当前ChannelHandler中不需要释放ByteBuf,由最后一个使用该ByteBuf的ChannelHandler释放。

c. 如果处理的ByteBuf是由decode()等会增加计数器的操作生成的,不再传递时,ByteBuf也要释放。

d. 如果不确定要不要释放,或者简化释放的过程,可以调用ReferenceCountUtil.release(ByteBuf)函数。

e. 也可以把ChannelHandler都承继自SimpleChannelInboundHandler虚类,该类会在channelRead函数中调用ReferenceCountUtil.release(msg)来帮助释放ByteBuf,如下代码所示,channelRead0(ctx, imsg)是一个虚函数,子类实现channelRead0函数用来完成处理逻辑。

使用引用计数的缺点在于容易产生内存泄露,因为JVM不知道引用计数的存在。当一个对象不可达时,JVM可能会收回该对象,但这个对象的引用计数可能还不是0,这就导致该对象从池里分配的空间不能归还到池里,从而导致内存泄露。

Netty提供了一种内存泄露检测机制,可以通过配置参数不同选择不同的检测级别,参数设置为java -Dio.netty.leakDetection.level=advanced

  • DISABLED :完全禁用内存泄露检测,不推荐
  • SIMPLE :抽样1%的ByteBuf,提示是否有内存泄露
  • ADVANCED :抽样1%的ByteBuf,提示哪里产生了内存泄露
  • PARANOID :对每一个ByteBu进行检测,提示哪里产生了内存泄露

我在测试时,直接提示了ByteBuf内存泄露的位置,如下,找到自己程序代码,看哪里有新生成的ByteBuf对象没有释放,主动释放一下,调用对象的release()函数,或者用工具类帮助释放ReferenceCountUtil.release(msg)。

引用了 Netty中ByteBuf内存泄露及释放解析 - zhaoshizi - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值