文档章节

深入研究Netty框架之ByteBuf家族

AbeJeffrey
 AbeJeffrey
发布于 2016/09/06 17:37
字数 1599
阅读 699
收藏 2

ByteBuf类继承关系图如下:

ReferenceCounted:对象引用计数器,初始化ReferenceCounted对象时,引用数量refCnt为1,调用retain()可增加refCnt,release()用于减少refCnt。refCnt为1时,说明对象实际不可达,release()方法将立即调用deallocate()释放对象。如果refCnt为0,说明对象被错误的引用。在AbstractReferenceCountedByteBuf源码分析小节将详细介绍ReferenceCounted的原理。

ByteBuf:实现接口ReferenceCounted和Comparable,实现ReferenceCounted使得ByteBuf具备引用计数的能力,方便跟踪ByteBuf对象分配和释放。

  • ByteBuf直接子类

EmptyByteBuf:用于构建空ByteBuf对象,capacity和maxCapacity均为0。

ReplayingDecoderBuffer:用于构建在IO阻塞条件下实现无阻塞解码的特殊ByteBuf对象,当要读取的数据还未接收完全时,抛出异常,交由ReplayingDecoder处理。

SwappedByteBuf:用于构建具有切换字节顺序功能的ByteBuf对象,默认ByteBuf对象使用BIG_ENDIAN(大字节序)存储数据,SwappedByteBuf可以在BIG_ENDIAN和LITTLE_ENDIAN之间自由切换。TCP/IP各层协议均采用网络字节序(BIG_ENDIAN),关于字节序的更多内容不详细介绍。

WrappedByteBuf:用于装饰ByteBuf对象,主要有AdvancedLeakAwareByteBuf、SimpleLeakAwareByteBuf和UnreleasableByteBuf三个子类。这里WrappedByteBuf使用装饰者模式装饰ByteBuf对象,AdvancedLeakAwareByteBuf用于对所有操作记录堆栈信息,方便监控内存泄漏;SimpleLeakAwareByteBuf只记录order(ByteOrder endianness)的堆栈信息;UnreleasableByteBuf用于阻止修改对象引用计数器refCnt的值。

AbstractByteBuf:提供ByteBuf的默认实现,同时组合ResourceLeakDetector和SwappedByteBuf的能力,ResourceLeakDetector是内存泄漏检测工具,SwappedByteBuf用于字节序不同时转换字节序。

  • AbstractByteBuf直接子类

AbstractDerivedByteBuf:提供派生ByteBuf的默认实现,主要有DuplicatedByteBuf、ReadOnlyByteBuf和SlicedByteBuf。

DuplicatedByteBuf使用装饰者模式创建ByteBuf的复制对象,使得复制后的对象与原对象共享缓冲区的内容,但是独立维护自己的readerIndex和writerIndex。部分源码如下:

    private final ByteBuf buffer;

    public DuplicatedByteBuf(ByteBuf buffer) {
        super(buffer.maxCapacity());
        //共享缓冲区内容
        if (buffer instanceof DuplicatedByteBuf) {
            this.buffer = ((DuplicatedByteBuf) buffer).buffer;
        } else {
            this.buffer = buffer;
        }
        //调用自身的setIndex方法维护readerIndex和writerIndex
        setIndex(buffer.readerIndex(), buffer.writerIndex());
    }
    //所有操作都是通过调用被装饰对象buffer的相应方法实现
    @Override
    public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
        buffer.getBytes(index, dst, dstIndex, length);
        return this;
    }
    @Override
    public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
        buffer.getBytes(index, dst, dstIndex, length);
        return this;
    }

ReadOnlyByteBuf使用装饰者模式创建ByteBuf的只读对象,该只读对象与原对象共享缓冲区的内容,但是独立维护自己的readerIndex和writerIndex,之后所有的写操作都被限制;部分源码如下:

    private final ByteBuf buffer;

    public ReadOnlyByteBuf(ByteBuf buffer) {
        super(buffer.maxCapacity());

        if (buffer instanceof ReadOnlyByteBuf || buffer instanceof DuplicatedByteBuf) {
            this.buffer = buffer.unwrap();
        } else {
            this.buffer = buffer;
        }
        setIndex(buffer.readerIndex(), buffer.writerIndex());
    }
    @Override
    protected void _setLong(int index, long value) {
        throw new ReadOnlyBufferException();
    }

    @Override
    public int setBytes(int index, InputStream in, int length) {
        throw new ReadOnlyBufferException();
    }

SlicedByteBuf使用装饰者模式创建ByteBuf的一个子区域ByteBuf对象,返回的ByteBuf对象与当前ByteBuf对象共享缓冲区的内容,但是维护自己独立的readerIndex和writerIndex,允许写操作。

AbstractReferenceCountedByteBuf:提供修改对象引用计数器相关操作的默认实现。

  • AbstractReferenceCountedByteBuf直接子类

CompositeByteBuf:用于将多个ByteBuf组合在一起,形成一个虚拟的ByteBuf对象,支持读写和动态扩展。内部使用List<Component>组合多个ByteBuf。推荐使用ByteBufAllocator的compositeBuffer()方法,Unpooled的工厂方法compositeBuffer()或wrappedBuffer(ByteBuf... buffers)创建CompositeByteBuf对象。

FixedCompositeByteBuf:用于将多个ByteBuf组合在一起,形成一个虚拟的只读ByteBuf对象,不允许写入和动态扩展。内部使用Object[]将多个ByteBuf组合在一起,一旦FixedCompositeByteBuf对象构建完成,则不会被更改。

PooledByteBuf<T>:基于内存池的ByteBuf,主要为了重用ByteBuf对象,提升内存的使用效率;适用于高负载,高并发的应用中。主要有PooledDirectByteBuf,PooledHeapByteBuf,PooledUnsafeDirectByteBuf三个子类,PooledDirectByteBuf是在堆外进行内存分配的内存池ByteBuf,PooledHeapByteBuf是基于堆内存分配内存池ByteBuf,PooledUnsafeDirectByteBuf也是在堆外进行内存分配的内存池ByteBuf,区别在于PooledUnsafeDirectByteBuf内部使用基于PlatformDependent相关操作实现ByteBuf,具有平台相关性。

ReadOnlyByteBufferBuf:只读ByteBuf,内部持有ByteBuffer对象,相关操作委托给ByteBuffer实现,该ByteBuf限内部使用,ReadOnlyByteBufferBuf还有一个子类ReadOnlyUnsafeDirectByteBuf。

UnpooledDirectByteBuf:在堆外进行内存分配的非内存池ByteBuf,内部持有ByteBuffer对象,相关操作委托给ByteBuffer实现。

UnpooledHeapByteBuf:基于堆内存分配非内存池ByteBuf,即内部持有byte数组。

UnpooledUnsafeDirectByteBuf:与UnpooledDirectByteBuf相同,区别在于UnpooledUnsafeDirectByteBuf内部使用基于PlatformDependent相关操作实现ByteBuf,具有平台相关性。

到此,ByteBuf继承家族的各个成员对应的相关功能已介绍完成。

总结:

从内存分配角度看,ByteBuf主要分为两类:

  • 堆内存(HeapByteBuf)字节缓冲区:特点是内存的分配和回收速度快,可以被JVM自动回收;缺点是进行Socket的I/O读写需要额外进行一次内存复制,即将内存对应的缓冲区复制到内核Channel中,性能会有一定程度下降。
  • 直接内存(DirectByteBuf)字节缓冲区:在堆外进行内存分配,相比堆内存,分配和回收速度稍慢。但用于Socket的I/O读写时,少一次内存复制,速度比堆内存字节缓冲区快。

经验表明,在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的编解码模块使用HeapByteBuf,这样组合可以达到性能最优。

从内存回收角度看,ByteBuf也分为两类:

  • 基于内存池的ByteBuf:优点是可以重用ByteBuf对象,通过自己维护一个内存池,可以循环利用创建的ByteBuf,提升内存的使用效率,降低由于高负载导致的频繁GC。适用于高负载,高并发的应用中。推荐使用基于内存池的ByteBuf。
  • 非内存池的ByteBuf:优点是管理和维护相对简单。

本节重点介绍ByteBuf继承家族的各个成员,详细功能后续将通过源码讲解,下一节介绍AbstractByteBuf源码。

欢迎指出本文有误的地方,转载请注明原文出处https://my.oschina.net/7001/blog/743240

© 著作权归作者所有

共有 人打赏支持
AbeJeffrey
粉丝 39
博文 43
码字总数 116095
作品 0
杭州
高级程序员
私信 提问
Netty精粹之玩转NIO缓冲区

在JAVA NIO相关的组件中,ByteBuffer是除了Selector、Channel之外的另一个很重要的组件,它是直接和Channel打交道的缓冲区,通常场景或是从ByteBuffer写入Channel,或是从Channel读入Buffer;...

Float_Luuu
2016/03/13
2.2K
0
深入研究Netty框架之ByteBuf功能原理及源码分析

ByteBuf功能原理 ByteBuf是一个byte数组的缓冲区,通过两个位置指针完成缓冲区的读写操作,读操作使用readerIndex,写操作使用writeIndex。 readerIndex和writeIndex初始取值均为0,写入数据...

AbeJeffrey
2016/09/04
409
0
Spark Netty与Jetty (源码阅读十一)

  spark呢,对Netty API又做了一层封装,那么Netty是什么呢~是个鬼。它基于NIO的服务端客户端框架,具体不再说了,下面开始。   创建了一个线程工厂,生成的线程都给定一个前缀名。   ...

雪童子
2016/12/08
0
0
《Netty In Action》第七章 编解码器Codec

本章介绍 Codec,编解码器 Decoder,解码器 Encoder,编码器 Netty提供了编解码器框架,使得编写自定义的编解码器很容易,并且也很容易重用和封装。本章讨论Netty的编解码器框架以及使用。 ...

残刃O
2018/02/05
5
0
初识Netty -- 基于Netty的DayTime时间服务器

1. 关于Netty的基本认知: 在JDK 1.4推出Java NIO之前,基于Java的所有Socket通信都采用的BIO(同步阻塞式IO),同步阻塞式IO存在巨大的性能和可靠性瓶颈,无法适用于高性能服务器的开发。虽...

一赫
2017/10/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

dockerfile 镜像构建(1)

通用dockerfile 利用已经编译好的.jar 来构建镜像。要构建的目录如下: [root@iZuf61quxhnlk9m2tkx16cZ demo_jar]# docker build -t demo:1 . 运行镜像: [root@iZuf61quxhnlk9m2tkx16cZ de...

Canaan_
25分钟前
0
0
Redis radix tree源码解析

Redis实现了不定长压缩前缀的radix tree,用在集群模式下存储slot对应的的所有key信息。本文将详述在Redis中如何实现radix tree。 核心数据结构 raxNode是radix tree的核心数据结构,其结构体...

阿里云云栖社区
28分钟前
5
0
vue import 传入变量

在做动态添加component的时候,传入变量就会报错,出现以下错误信息: vue-router.esm.js?fe87:1921 Error: Cannot find module '@/components/index'. at eval (eval at ./src/components ......

朝如青丝暮成雪
30分钟前
0
0
Flutter开发 Dio拦截器实现token验证过期的功能

前言: 之前分享过在Android中使用Retrofit实现token失效刷新的处理方案,现在Flutter项目也有“token验证过期”的需求,所以接下来我简单总结一下在Flutter项目中如何实现自动刷新token并重...

EmilyWu
31分钟前
6
0
final Map可以修改内容,final 常量不能修改

1.final Map 可以put元素,但是不可以重新赋值 如: final Map map = new HashMap(); map = new HashMap();//不可以 因为栈中变量map引用地址不能修改 2.final str = “aa”; str = "bb";/......

qimh
34分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部