
Netty 作为一个优秀网络框架,其高效的 内存 *** 作 也是使其变得 高性能 的很重要原因之一。
众所周知, Java 的 NIO 中提供了类 ByteBuffer 作为字节的容器,但是 *** 作非常的复杂,Netty针对 ByteBuffer 设计了一个替代类 ByteBuf ,方便开发者 *** 作字节。
对于任意一个 ByteBuf 对象,都拥有三个非常重要的属性:
ByteBuf 对象每读取一个byte的数据,readerIndex就会往前推进,直到readerIndex到达capacity的值,所有的数据的数据都被读取完, ByteBuf 不可再被读取。可以通过 readableBytes() 方法获取 readerIndex 的值。
相同地,writerIndex记录了 ByteBuf 对象使用了多少数据,可以通过 writableBytes() 方法获取writerIndex的值。每当 ByteBuf 被写入了多少数据,writerIndex就会往前推进,直到值到达capacity的值, ByteBuf 会自动对空间进行扩容。
对于任意一个 ByteBuf 对象,我们都可以根据它的索引通过 getByte() 方法随机访问中间的数据。随机访问不会改变 readerIndex 的值。
通过 array() 方法可以直接获取, ByteBuf 中的Byte数组信息。
Netty的“Zero-Copy”设计非常出名,这主要就是依赖了Netty中 ByteBuf 的设计。 ByteBuf 主要有以下几种模式:
顾名思义,这个模式下的字节是在Jvm的堆区 *** 作的,也是最常见的内存 *** 作了。
在JDK1.4中,Java引入了一种 直接内存 ,NIO可以通过 本地方法 分配一些堆外的直接内存,这块内存区不受Jvm的控制,理论上的无限的。
对于网络Socket通信来说,这种内存区域的好处是Java在通信中,数据不必从Jvm中拷贝一份到系统的直接内存区上, *** 作系统的Socket接口可以直接处理这份在直接内存的数据。同时由于数据在堆外,也避免了频繁GC对这块区域的影响。
ByteBuf 提供了 Direct Buffer 模式,我们可以直接通过 ByteBuf *** 作 直接内存 。
Direct Buffer 模式下,由于数据不在堆上面, ByteBuf 是不可以直接使用 array() 方法获取数据的。
在TCP协议中,一份完整的数据总是被拆成好几个包被发送或者接收,一般情况下,程序会通过内存拷贝的方式将一组数据拷贝到一个大的数组中,形成一份完整的数据。
而Composite buffer模式可以聚合多个ByteBuffer对象,将这组数据的引用收集到一个 ByteBuf 对象中, 避免 了数据的拷贝。
当然为了避免Netty本身内存使用过度,Netty内部对所有的内存做了池化。通过 ByteBufAllocator 类,我们可以分配一块被池化的内存,从而减少分配和释放内存的开销。
如果我们希望使用一块新的内存,或者对一个已经存在的内存进行包装,那么我们可以使用 Unpooled 类来分配内存:
传输的对象单位 -- 字节流 00000000记住一点即可,底层传输时数据均为二进制,所以调试的时候不要在意IDE工具显示堆栈的进制是什么,底层形态仍然是二进制。
在做byte ->int类型转换时,JVM会做一个补位处理,
(注:补位是补1还是补0,取决于byte的最高位是1还是0)
以协议中出现的0x8A的协议号为例,转成二进制为:10001010,
如果直接赋值int值后是其实在计算机存储的是11111111 11111111 11111111 10001010(32位),
这个时候其实与最初的0x8A已经完全不等了,所以我们需要对其进行与0xFF做与运算,
可以将高24位置为0,低8位保持不变,这样做就可以保证和二进制补码的一致了。
如果我们想读取3个字节的整型数值,可以使用:
依次类推,读5个字节,6个字节,7个字节,8个字节都可以这样先按上述的几个方法读取连续几位,再移位,再读取后面连续的几位,并相加。
如果想获取某个bit位是1还是0,可以用以下方法:
也可以采用先移位,再&0x01。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)