Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述

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 leakDetector =
            ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);

    int readerIndex;
    int writerIndex;
    private int markedReaderIndex;
    private int markedWriterIndex;
    private int maxCapacity;

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

Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述

当你从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(一)—ByteBuf中API及类型概述

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及类型概述

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

Original: https://www.cnblogs.com/dafanjoy/p/13530708.html
Author: DaFanJoy
Title: Netty源码分析之ByteBuf(一)—ByteBuf中API及类型概述

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/593439/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Nginx 源码分析– 内存池(pool)的分析 一

    Nginx内存池(pool)的管理的实现主要写在ngx_palloc.c这个文件中,为了明晰的看该文件中的函数之间的关系,我们描绘出以下一张简单的示意图: 图1 Nginx内存池(…

    Java 2023年6月15日
    0106
  • java使用POI操作XWPFDocument 生成Word实战(一)

    注:我使用的word 2016功能简介:(1)使用jsoup解析html得到我用来生成word的文本(这个你们可以忽略)(2)生成word、设置页边距、设置页脚(页码),设置页码(…

    Java 2023年6月7日
    0114
  • Spring Boot 入门(三)部署Redis服务

    我用的是腾讯云(轻量应用服务),操作系统CentOS 8.0 64bit *安装Redis服务 Redis下载地址:https://redis.io/download 我是直接在服…

    Java 2023年6月5日
    087
  • PythonWeb开发

    1.什么是Flask?有什么优点? 2.Django和Flask有什么区别? 3.Flask-WTF是什么,有什么特点? 4.Flask脚本的常用方式是什么? 5.如何在Flask…

    Java 2023年6月7日
    098
  • 低版本idea中SpringBoot项目启动失败,提示找不到 javax/servlet/ServletContext类

    转载:https://www.jiweichengzhu.com/article/322378a857194c1580693944eff9468f Original: https:…

    Java 2023年5月29日
    099
  • C# 线程手册 第六章 线程调试与跟踪 DataImport 例子

    现在我们要集中精力实现一个实战实例来描述到目前为止我们已经看过的内容。这里要实现的DataImport 例子是那种等待文件到达指定目录然后将其导入到一个SQL Server 数据库…

    Java 2023年5月29日
    072
  • java并发和排序的简单例子(Runnable+TreeSet)

    很多时候并发需要考虑线程安全,但也有很多时候和线程安全毛关系都没有,因为并发最大的作用是并行,线程安全仅仅是并发的一个子话题。 例如常常会用于并发运算,并发i/o。 下文是一个练习…

    Java 2023年6月9日
    095
  • 图片在容器内水平垂直居中显示

    图片在容器内水平垂直居中显示 效果: .imgbox { border: 1px solid #dcdfe6; position: relative; width: 150px; …

    Java 2023年6月9日
    084
  • Java对象的内存布局

    Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。 Mark Word在32位J…

    Java 2023年5月29日
    087
  • VMware虚拟机 NAT模式 配置静态ip

    前言:Ubuntu 16.04 VMware虚拟机 NAT模式 配置静态ip,这个问题困扰我好长时间,桥接的静态ip我会了,然而用NAT 的方式配置集群会更好。(NAT 方式客户机…

    Java 2023年5月30日
    074
  • 阿尔兹海默症_记不住的java8 List操作

    一丢丢废话 使用java8对list操作不是很方便嘛但是呢,脑子不好,似乎老是记不住 取出List中的某一列作为一个新的list 假设有一个实例User,User中有成员变量nam…

    Java 2023年6月7日
    089
  • JVM内存区域与内存溢出异常(1)

    运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据 区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的 启动…

    Java 2023年6月13日
    080
  • HashMap底层原理及jdk1.8源码解读

    一、前言 写在前面:小编码字收集资料花了一天的时间整理出来,对你有帮助一键三连走一波哈,谢谢啦!! HashMap在我们日常开发中可谓经常遇到,HashMap 源码和底层原理在现在…

    Java 2023年6月15日
    0101
  • Java基础–int与Integer区别

    看过java相关入门开发的书籍的应该都知道,这个标题其实涉及到两个问题: 1. java基本数据类型和引用类型 2. 自动拆箱和自动装箱 下面就这两个问题进行一下总结复习。 一、j…

    Java 2023年6月5日
    074
  • 聊一聊过滤器与拦截器

    过滤器 Filter 面试官:用过过滤器吧,介绍一下过滤器。John同学(心中窃喜):用过,我经常用它来净化水 😁… 面试官:今天的面试到此结束,回去等通知吧。John…

    Java 2023年6月9日
    080
  • Linux安装mysql8.0.29详细教程

    我在上午卸载了陪伴我多年的mysql5.7,现在准备安装mysql8.0。 1)根据自己电脑的位数和你cpu架构相符的安装 2))选完之后找到RPM Bundle点击下载 1、登录…

    Java 2023年6月13日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球