ByteBuf是Netty中主要的数据容器与操作工具,也是Netty内存管理优化的具体实现,本章我们先从整体上对ByteBuf进行一个概述;

AbstractByteBuf是整个ByteBuf的框架类,定义了各种重要的标志位与API供具体的实现类使用与实现;下面我们就从AbstractByteBuf类入手对ByteBuf的读写机制与API进行一个简单的介绍

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class);
private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible";
private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible";
static final boolean checkAccessible; // accessed from CompositeByteBuf
private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds";
private static final boolean checkBounds; static {
if (SystemPropertyUtil.contains(PROP_CHECK_ACCESSIBLE)) {
checkAccessible = SystemPropertyUtil.getBoolean(PROP_CHECK_ACCESSIBLE, true);
} else {
checkAccessible = SystemPropertyUtil.getBoolean(LEGACY_PROP_CHECK_ACCESSIBLE, true);
}
checkBounds = SystemPropertyUtil.getBoolean(PROP_CHECK_BOUNDS, true);
if (logger.isDebugEnabled()) {
logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible);
logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds);
}
} static final ResourceLeakDetector<ByteBuf> leakDetector =
ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class); int readerIndex;
int writerIndex;
private int markedReaderIndex;
private int markedWriterIndex;
private int maxCapacity;

在上面的代码中,我们需要知道ByteBu维护了两个不同的索引readerIndex与writerIndex,这两个索引默认都是从0开始,一个用于读取,一个用于写入。

当你从ByteBuf读取数据时,readerIndex会递增已经读取的字节数,同理当你写入数据时,writerIndex也会随之递增。可以说ByteBuf中各种读写API都是基于readerIndex与writerIndex来控制的。从索引操作上,ByteBuf中API基本分为两大部分,会引发索引值递增的read(读)和write(写操作),反之不会引发索引值递增的get或set操作。下面我们看下ByteBu中常用API的参考说明

ByteBuf API

read操作

readBoolean() 返回当前readIndex的Boolean值,readIndex增加1
readByte(int) 返回当前readIndex处的字节值,readIndex增加1
readUnsignedByte() 返回当前readIndex处的无符号字节值,readIndex增加1
readInt() 返回当前readIndex处的int值,readIndex增加4
readUnsignedInt() 返回当前readIndex处的无符号int值,返回类型为long,readIndex增加4
readLong() 返回当前readIndex处的long值,readIndex增加8
readShort()

返回当前readIndex处的short值,readIndex增加2

readUnsignedShort() 返回当前readIndex处的short值,readIndex增加2

wirte操作

writeBoolean() 在当前writerIndex处写入一个Boolean值(1或0),writerIndex增加1
writeByte(int) 在当前writerIndex处写入一个byte值,writerIndex增加1
writeShort(int) 在当前writerIndex处写入一个short值,writerIndex增加2
writeInt(int) 在当前writerIndex处写入一个int值,writerIndex增加4
writeLong(int) 在当前writerIndex处写入一个long值,writerIndex增加4

get操作

getBoolean(int) 返回给的索引处的Boolean值,readIndex值不变
getByte(int) 返回给的索引处的Byte值,readIndex值不变
getShort(int) 返回给的索引处的short值,readIndex值不变
getInt(int) 返回给的索引处的int值,readIndex值不变
getLong(int) 返回给的索引处的long值,readIndex值不变

set操作

setBoolean(int,boolean) 在指定索引处设置一个Boolean值(1或0),writerIndex值不变
setByte(int,int) 在指定索引处设置一个byte值,writerIndex值不变
setShort(int,int) 在指定索引处设置一个short值,writerIndex值不变
setInt(int,int) 在指定索引处设置一个int值,writerIndex值不变
setLong(int,int) 在指定索引处设置一个long值,writerIndex值不变

ByteBuf类型

Netty中ByteBuf相关实现类的UML图

Netty在构建ByteBuf时,有以下多种分类:

1、从内存类型上,分为堆内存与直接内存,HeapByteBuf与DirectByteBuf

我们知道Java中大部分对象都是在堆内存中存储,由jvm统一管理,但NIO中 的 ByteBuffer 类允许 JVM 实现通过本地调用来分配内存,Netty中对基于直接内存的ByteBuffer也进行了封装,这主要是为了避免在每次调用本地 I/O 操作之前(或者之后)将缓冲区的内容复 制到一个中间缓冲区(或者从中间缓冲区把内容复制到缓冲区),直接缓冲区不会占用堆的容量。事实上,在通过套接字发送它之前,JVM将会在内部把你的缓冲 区复制到一个直接缓冲区中。所以如果使用直接缓冲区可以节约一次拷贝,提高IO操作性能。使用直接内存的优缺点如下:

(1)优点:由于数据直接在内存中,不存在从JVM拷贝数据到直接缓冲区的过程,提高IO操作性能。

(2)缺点:相对于基于堆的缓冲区,它们的分配和释放都较为昂贵,同时由于直接内存不受JVM垃圾回收统一管理,需要自己手动回收,需要特别注意内存泄露的问题。

2、从分配模式上,分为池化与非池化;PooledByteBuf与UnpooledByteBuf

对于频繁的申请与释放内存带来的性能损耗与碎片化问题,Netty基于池化思想通过预先申请一块专用内存地址作为内存池进行管理,从而不需要每次都进行分配和释放。

从上面的UML图中可以看到,基于AbstractByteBuf父类针对直接内存与堆内存,也都有其对应的池化与非池化实现类;

PooledByteBuf  下有  PooledUnsafeDirectByteBuf、PooledHeapByteBuf、PooledDirectByteBuf三个子类实现

UnpooledByteBuf 下则是 UnpooledDirectByteBuf及其子类UnpooledUnsafeDirectByteBuf 与 UnpooledHeapByteBuf及其子类UnpooledUnsafeHeapByteBuf

3、从具体的操作类上,分为Unsafe与非Unsafe

从上面的子类实现中,我们发现每种分类中又包含Usafe与非Unsafe的区别,我们知道java可以通过Unsafe类直接操作内存区域,所以这些类的区别就是在于是调用jdk的Unsafe直接去操作对象的内存地址还是通过jdk封装的安全方式操作内存。

我们通过PooledByteBuf下Unsafe与非Unsafe实现类的getByte方法,看下具体的区别

PooledUnsafeHeapByteBuf

    @Override
protected byte _getByte(int index) {
return UnsafeByteBufUtil.getByte(addr(index));
}

Netty中封装了一个UnsafeByteBufUtil类,进入内部实现看到调用的是Unsafe对象进行具体操作

    static byte getByte(long address) {
return UNSAFE.getByte(address);
}

PooledHeapByteBuf

跟踪其内部代码则可以看到字节是基于数组操作的

    @Override
protected byte _getByte(int index) {
return HeapByteBufUtil.getByte(memory, idx(index));
} static byte getByte(byte[] memory, int index) {
return memory[index];
}

PooledDirectByteBuf

针对DirectByteBuf大家需要灵活理解,PooledUnsafeDirectByteBuf 与PooledUnsafeDirectByteBuf的区别一个是UnsafeByteBufUtil类直接操作,一个是使用java NIO中的DirectByteBuf类进行操作,但因为要操作直接内存,最后还都是要基于jdk的unsafe类实现;

对比他们两种实现,可以看出Unsafe与非Unsafe的区别,前者是通过jdk的Unsafe类去操作数据,后者直接通过数组或者jdk底层的DirectByteBuf去操作数据。

ByteBuf作为Netty中数据操作与内存分配的具体实现,是Netty中最为底层的也是最精细的一部分,本文只是对ByteBuf的一个简单概述,后续我们会进一步对其进行探索与剖析,更好的展示Netty内部具体实现,希望对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

转载说明:未经授权不得转载,授权后务必注明来源(注明:来源于公众号:架构空间, 作者:大凡)

Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述的更多相关文章

  1. Netty源码分析第5章(ByteBuf)---->第1节: AbstractByteBuf

    Netty源码分析第五章: ByteBuf 概述: 熟悉Nio的小伙伴应该对jdk底层byteBuffer不会陌生, 也就是字节缓冲区, 主要用于对网络底层io进行读写, 当channel中有数据时, ...

  2. Netty源码分析第5章(ByteBuf)---->第2节: ByteBuf的分类

    Netty源码分析第五章: ByteBuf 第二节: ByteBuf的分类 上一小节简单介绍了AbstractByteBuf这个抽象类, 这一小节对其子类的分类做一个简单的介绍 ByteBuf根据不同 ...

  3. Netty源码分析第5章(ByteBuf)---->第3节: 缓冲区分配器

    Netty源码分析第五章: ByteBuf 第三节: 缓冲区分配器 缓冲区分配器, 顾明思议就是分配缓冲区的工具, 在netty中, 缓冲区分配器的顶级抽象是接口ByteBufAllocator, 里 ...

  4. Netty源码分析第5章(ByteBuf)---->第4节: PooledByteBufAllocator简述

    Netty源码分析第五章: ByteBuf 第四节: PooledByteBufAllocator简述 上一小节简单介绍了ByteBufAllocator以及其子类UnPooledByteBufAll ...

  5. Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述

    Netty源码分析第五章: ByteBuf 第五节: directArena分配缓冲区概述 上一小节简单分析了PooledByteBufAllocator中, 线程局部缓存和arean的相关逻辑, 这 ...

  6. Netty源码分析第5章(ByteBuf)---->第6节: 命中缓存的分配

    Netty源码分析第6章: ByteBuf 第六节: 命中缓存的分配 上一小节简单分析了directArena内存分配大概流程, 知道其先命中缓存, 如果命中不到, 则区分配一款连续内存, 这一小节带 ...

  7. Netty源码分析第5章(ByteBuf)---->第7节: page级别的内存分配

    Netty源码分析第五章: ByteBuf 第六节: page级别的内存分配 前面小节我们剖析过命中缓存的内存分配逻辑, 前提是如果缓存中有数据, 那么缓存中没有数据, netty是如何开辟一块内存进 ...

  8. Netty源码分析第5章(ByteBuf)---->第8节: subPage级别的内存分配

    Netty源码分析第五章: ByteBuf 第八节: subPage级别的内存分配 上一小节我们剖析了page级别的内存分配逻辑, 这一小节带大家剖析有关subPage级别的内存分配 通过之前的学习我 ...

  9. Netty源码分析第5章(ByteBuf)---->第9节: ByteBuf回收

    Netty源码分析第五章: ByteBuf 第九节: ByteBuf回收 之前的章节我们提到过, 堆外内存是不受jvm垃圾回收机制控制的, 所以我们分配一块堆外内存进行ByteBuf操作时, 使用完毕 ...

  10. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

随机推荐

  1. 从别人的代码中学习golang系列--03

    这篇博客还是整理从https://github.com/LyricTian/gin-admin 这个项目中学习的golang相关知识. 作者在项目中使用了 github.com/casbin/casb ...

  2. PHP代码混淆与加密——php screw plus

    php是一个开源的.广受欢迎的语言,php应用常常是以代码明文的发式发布,但是有时候对于发布的代码我们想要进行保护,需要对php代码进行混淆与加密,让增加破解者的逆向难度,从而达到保护自己成果的目的. ...

  3. ThreadLocal面试六连问

    转自:码农沉思录 中高级阶段开发者出去面试,应该躲不开ThreadLocal相关问题,本文就常见问题做出一些解答,欢迎留言探讨. ThreadLocal为Java并发提供了一个新的思路, 它用来存储T ...

  4. Web优化躬行记(3)——图像和网络

    一.图像 1)响应式图像 浏览器根据屏幕大小.设备像素比.横竖屏自动加载合适的图像. 响应式的功能可以通过srcset和sizes两个新属性实现. 前者可指定选择的图像以及其大小,后者会定义一组媒体条 ...

  5. R的操作入门熟悉

    产生向量: a=c(1,2,3) //产生 1,2,3向量 a=1:10-1 //产生 0 - 9数字 a=seq(5,20,by=2) // 以2增长 a=seq(5,120,length=10) ...

  6. Linux快速搭建C/C++开发环境

    导读:越来越多的程序员在Linux下进行C/C++的开发.本文以CentOS 7为例,教你快速搭建一个vi + gcc/g++ + Make + valgrind的开发环境. 本文字数:1500,阅读 ...

  7. MSF常用命令备忘录

    msf下的命令 set session x:设置要攻击的session #监听端口反弹PHP shell use exploit/multi/handler set payload php/meter ...

  8. 关于C#调用非托管DLL,报“内存已损坏的”坑,坑,坑

    因客户需求,与第三方对接,调用非托管DLL,之前正常对接的程序,却总是报“内存已损坏的异常”,程序进程直接死掉,折腾到这个点(2018-05-11 00:26),终于尘埃落定,直接上程序. 之前的程序 ...

  9. 面试题-JavaSE语法

    1.Java 有没有 goto 语句? goto 是 Java 中的保留字,在目前版本的 Java 中没有使用.根据 James Gosling(Java 之父)编写的<The Java  Pr ...

  10. SpringCloudAlibaba-服务网关Gateway

    一:网关简介 在微服务架构中,一个系统会被拆分为很多个微服务.那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用.这样的话会产生很多问 ...