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)。