Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >netty bytebuffer_netty源码剖析与实战

netty bytebuffer_netty源码剖析与实战

作者头像
全栈程序员站长
发布于 2022-11-04 11:54:07
发布于 2022-11-04 11:54:07
29900
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

一、背景简介

ByteBuf,顾名思义,就是字节缓冲区,是Netty中非常重要的一个组件。熟悉jdk NIO的同学应该知道ByteBuffer,正是因为jdk原生ByteBuffer使用比较复杂,某些场景下性能不是太好,netty开发团队重新设计了ByteBuf用以替代原生ByteBuffer。

二、ByteBuf和ByteBuffer对比

下面用图示来展示ByteBuf和ByteBuffer工作原理:

①、ByteBuffer

ByteBuffer依靠flip()来切换模式,在读模式下调用flip()切换为写模式,在写模式下limit和capacity相等,position标识当前写的位置。在写模式下调用flip()切换为读模式,在读模式下position回到起始位置开始读,limit回到position位置表示能读到多少数据,capacity不变表示缓存区容量大小。

capacity:在读/写模式下都是固定的,就是缓冲区容量大小。

position:读/写位置指针,表示当前读(写)到什么位置。

limit:在写模式下表示最多能写入多少数据,此时和capacity相同。在读模式下表示最多能读多少数据,此时它的值等于缓存区中实际数据量的大小。

②、ByteBuf

ByteBuf主要是通过readerIndex 和 writerIndex两个指针进行数据的读和写,整个ByteBuf被这两个指针最多分成三个部分,分别是可丢弃部分,可读部分和可写部分

刚初始化的时候,整个缓冲区还没有数据,读写指针都指向0,所有的内容都是可写部分,此时还没有可读部分和可丢弃部分,如下:

当写完N个字节数据后,读指针仍然是0,因为还没有开始进行读事件,写指针向后移动了N个字节的位置,如下:

当开始读数据并且读取M个字节数据之后(M<N)写指针位置不变,读指针后移动了M个字节的位置,如下:

当可丢弃部分数据被清空之后,readerindex重新回到起始位置,writerindex的位置为writerindex的值减去之前的readerindex,也就是M,相关图示如下:

调用clear之后,writerindex和readerinde全部复位为0。它不会清除缓冲区内容(例如,用填充0),而只是清除两个指针。更改的读写指针的值,每个位置上原本的字节内容并没有发生改变,只是变成了可写状态而已。另请注意,此操作的语义不同于Buffer.clear()。

三、源码

明白了ByteBuf工作原理之后,ByteBuf相关的api就很好理解了,在此附上netty官方api文档,以供参阅:

https://netty.io/4.1/api/overview-summary.html。

我们在这里看下netty扩容相关源码逻辑。

扩容肯定是在写入数据的时候会由相关逻辑判断,我们随便进入一个写入字节的api方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract ByteBuf writeBytes(byte[] src);

进入到其抽象子类AbstractByteBuf中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public ByteBuf writeBytes(byte[] src) {
        writeBytes(src, 0, src.length);
        return this;
    }
    @Override
    public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {
        ensureAccessible();
        ensureWritable(length);
        setBytes(writerIndex, src, srcIndex, length);
        writerIndex += length;
        return this;
    }

首先ensureAccessible进行安全校验,每种尝试访问缓冲区内容的方法都应调用此方法,以检查缓冲区是否已释放。然后ensureWritable判断是否可写,扩容相关逻辑就在这里进行判断,如果缓冲区可写执行setBytes进行数据写入,然后writerindex向后移动length的位置,最后将ByteBuf对象进行返回。我们重点看ensureWritable。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public ByteBuf ensureWritable(int minWritableBytes) {
        if (minWritableBytes < 0) {
            throw new IllegalArgumentException(String.format(
                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
        }
        ensureWritable0(minWritableBytes);
        return this;
    }

直接进入ensureWritable0(minWritableBytes)方法中,此时minWritableBytes就是我们计划需要申请的内存大小空间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private void ensureWritable0(int minWritableBytes) {
         // 安全检查,保证写入之前是可访问的
         //ensureAccessible();// 可写,不必扩容
        if (minWritableBytes <= writableBytes()) {
            return;
        }//下标越界
        if (minWritableBytes > maxCapacity - writerIndex) {
            throw new IndexOutOfBoundsException(String.format(
                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                    writerIndex, minWritableBytes, maxCapacity, this));
        }//达到临界条件,开始执行扩容逻辑
        // 计算新的容量,实际上为当前容量扩容至2的幂次方大小(具体是多少需要进行后续判断和计算)
        int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
        // 扩容后的容量
        capacity(newCapacity);
    }

可以看到真正开辟内存空间新容量逻辑处理的是 alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity)执行的,进入到方法里面。

来到其实现类AbstractByteBufAllocator的calculateNewCapacity方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     @Override  
    public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
        if (minNewCapacity < 0) {
            throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expectd: 0+)");
        }
        if (minNewCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
                    minNewCapacity, maxCapacity));
        }
        // 扩容的阈值,4兆字节大小
        final int threshold = 1048576 * 4;
        if (minNewCapacity == threshold) {
            return threshold;
        }
       //如果计划一共需要的内存容量大小大于阈值,则需要和最大容量j进行比较
        if (minNewCapacity > threshold) {
            int newCapacity = minNewCapacity / threshold * threshold;
            if (newCapacity + threshold > maxCapacity) {
                newCapacity = maxCapacity;
            } else {
                newCapacity += threshold;
            }
            return newCapacity;
        }//如果计划一共需要的内存容量大小小于阈值,则以64为基数进行倍增
        int newCapacity = 64;
        while (newCapacity < minNewCapacity) {
            newCapacity <<= 1;
        }
        return Math.min(newCapacity, maxCapacity);
    }

minNewCapacity是我们计划一共需要的内存容量大小,maxCapacity是最大缓冲区容量大小。首先判断minNewCapacity 是否小于零或者minNewCapacity 是否大于maxCapacity,满足任一都抛出异常信息,然后判断我们计划一共需要的内存容量大小minNewCapacity 是否等于了阈值4M:

①、如果等于了阈值,新容量大小就是阈值4M。

②、如果计划一共需要的内存容量大小大于阈值,则maxCapacity和minNewCapacity 相对于阈值的整数倍再加上一个阈值进行大小判断,如果大于maxCapacity,则新容量最大就是maxCapacity,返回maxCapacity,如果小于maxCapacity,则相当于按照阈值的2倍进行扩容。

③、如果计划一共需要的内存容量大小小于阈值,则以64为基数只要小于我们计划需要的内存容量大小,就2倍扩容,最后选取循环后的扩容值和最大值两个值其中的较小者。

至此扩容就完成了,总结来说就是在扩容过程中有一个扩容需要容量的一个阈值4M,如果我们需要的内存空间等于这个阈值,那么扩容后的容量就是阈值大小,如果我们需要的内存容量大小大于阈值或者小于阈值,其扩容逻辑判断和扩容后返回的容量大小是不同的。但是最终扩容后的容量大小总是2的幂次方大小并且不会比maxCapacity大。

4、ByteBuf主要的继承关系

从内存分配的角度看,ByteBuf可以分为两类

(1)堆内存(HeapByteBuf)字节缓冲区:特点是内存的分配和回收速度快,可以被JVM自动收回;缺点就是如果进行Socket的I/O读写,需要额外做一次内存复制,将堆内存对应的缓冲区复制到内核Chanenel中,性能会有一定程度的下降。

(2)直接内存(DirectByteBuf) 字节缓冲区:非堆内存,它在堆外进行内存分配,相比于堆内存,它的分配和回收速度会慢一些,但是将它写入或者从Socket Channel中读取时,由于少了一次内存复制,速度比堆内存快。

正式因为各有利弊,所以Netty提供了多种ByteBuf供开发者使用,经验表明,ByteBuf的最佳实践是在I/O通信线程的读写缓冲区使用DirectByteBuf,后端业务消息的编解码模块使用HeapByteBuf,这样组合可以达到性能最优。

从内存回收角度看,ByteBuf也可以分为两类:基于对象池的ByteBuf和普通ByteBuf。两者的主要区别就是基于对象池的ByteBuf可以重用ByteBuf对象,它自己维护了一个内存池,可以循环利用创建的ByteBuf,提升内存的使用效率,降低由于高负载导致的频繁GC。测试表名使用内存池后的Netty在高负载、大并发的冲击下内存和GC更加平稳。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/191833.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月19日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Netty Review - ByteBuf扩容机制源码解析
ByteBuf 扩容机制是指在写入数据时,如果当前容量不足以容纳新增的数据,则需要进行动态扩容,以适应数据量的增长。
小小工匠
2024/05/26
1820
Netty Review - ByteBuf扩容机制源码解析
Netty 之 ByteBuf 分析
Netty 中的 ByteBuf 和 NIO 中的 ByteBuffer 的区别。
java404
2019/02/26
1.5K0
Netty原理:ByteBuf对Nio bytebuffer做了什么导致效率提升?
A 长度固定,无法动态的扩容和缩容,缺乏灵活性 B 使用一个position记录读写的索引位置,在读写模式切换时需手动调用flip方法,增加了使用的复杂度。 C 功能有限,使用过程中往往需要自行封装
冷环渊
2021/11/24
6990
Netty原理:ByteBuf对Nio bytebuffer做了什么导致效率提升?
【Netty】「源码解析」(一)ByteBuf 的动态扩容策略与实现原理
本篇博文是《从0到1学习 Netty》中源码系列的第一篇博文,主要内容是通过源码逐步讲解 Netty 中 ByteBuf 的动态扩容机制,并结合应用案例加以验证,往期系列文章请访问博主的 Netty 专栏,博文中的所有代码全部收集在博主的 GitHub 仓库中;
sidiot
2023/08/30
4580
【Netty】「源码解析」(一)ByteBuf 的动态扩容策略与实现原理
Netty8# Netty之ByteBuf初探
字节的流动形成了流,Netty作为优秀的通信框架他的字节是如何流动的,本文就理一下这个事。梳理完Netty的字节流动与JDK提供的ByteBuffer一对比看下Netty方便在哪里。本分从官方文档概念原理入手梳理,然后看下源码解读下这些原理如何实现的,体验一把Netty写入数据自动扩容,探究下这个过程如何实现的。
瓜农老梁
2021/01/12
4180
bytebuffer是什么_byte与bit
ByteBuffer是JDK NIO中提供的Java.nio.Buffer, 在内存中预留指定大小的存储空间来存放临时数据,其他Buffer 的子类有:CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer
全栈程序员站长
2022/09/28
4650
bytebuffer是什么_byte与bit
Netty整理(三) 顶
JDK ByteBuffer 共用读写索引,每次读写操作都需要Flip() 扩容麻烦,而且扩容后容易造成浪费 关于ByteBuffer的使用方法可以参考序列化和反序列化的三种方法 ,里面有Netty 3的ChannelBuffer,因为现在Netty 3用的比较少,当作参考就好。
算法之名
2019/10/09
5010
Netty整理(三)
                                                                            顶
【Netty】深入分析ByteBuf
之前的关于【Netty】的文章我们已经了解到 Netty 里面数据读写是以 ByteBuf 为单位进行交互的,这一小节,我们就来详细剖析一下 ByteBuf 之前文章链接:
周三不加班
2019/09/04
1.2K0
【Netty】深入分析ByteBuf
【Netty】「萌新入门」(六)ByteBuf 的基本使用
本篇博文是《从0到1学习 Netty》中入门系列的第六篇博文,主要内容是介绍 Netty 中 ByteBuf 的基本使用,包含其组成、创建、写入和读取,通过源码分析和应用案例进行详细讲解,往期系列文章请访问博主的 Netty 专栏,博文中的所有代码全部收集在博主的 GitHub 仓库中;
sidiot
2023/08/30
1.1K0
【Netty】「萌新入门」(六)ByteBuf 的基本使用
Netty ByteBuf源码解读
  Netty里的ByteBuf主要用于发送或接收消息。在JDK里有相似功能的类java.nio.ByteBuffer。由于JDK在设计ByteBuffer API的时候对用户不太友好,主要表现在1:写读切换的时候需要调用flip方法。2:初使化的时候长度便固定了,没有提供自动扩容的功能。而Netty在设计ByteBuf的时候考虑到API在使用上的便利,对上面提到的两个问题很好的进行了规避。
良辰美景TT
2018/09/11
7270
Netty ByteBuf源码解读
聊一聊 Netty 数据搬运工 ByteBuf 体系的设计与实现
时光芿苒,岁月如梭,好久没有给大家更新 Netty 相关的文章了,在断更 Netty 的这段日子里,笔者一直在持续更新 Linux 内存管理相关的文章 ,目前为止,算是将 Linux 内存管理子系统相关的主干源码较为完整的给大家呈现了出来,同时也结识了很多喜欢内核的读者,经常在后台留言讨论一些代码的设计细节,在这个过程中,我们相互分享,相互学习,浓浓的感受到了大家对技术那份纯粹的热爱,对于我自己来说,也是一种激励,学习,提高的机会。
bin的技术小屋
2024/08/16
3870
聊一聊 Netty 数据搬运工 ByteBuf 体系的设计与实现
targetCapacity 在(capacity , maxCapacity] ,进行扩容
final void ensureWritable0(int minWritableBytes) {
用户7737280
2024/08/21
1180
Netty技术全解析:ByteBuf源码视角下的技术与实现
Netty,作为一个高性能的网络编程框架,其核心组件之一便是ByteBuf。ByteBuf为字节数据的处理提供了一个比Java NIO中的ByteBuffer更加灵活和高效的容器。本文将从源码的角度,深入探讨ByteBuf的作用、功能、原理及其主要实现类,并对比它与Java NIO中的ByteBuffer。
公众号:码到三十五
2024/08/06
3060
capacity 与 JDK ByteBuffer 中的 capacity 含义保持一致
public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
用户7737280
2024/08/21
1260
Netty技术全解析:UnpooledHeapByteBuf源码视角下的详解
Netty,作为一款高性能的网络编程框架,提供了丰富的字节缓冲区(ByteBuf)实现,以满足不同的内存管理需求。其中,UnpooledHeapByteBuf是Netty提供的一种非池化的堆内存ByteBuf实现。本文将结合源码,深入探讨UnpooledHeapByteBuf的工作原理、实现细节以及其在Netty中的作用
公众号:码到三十五
2024/08/06
2560
Netty in Action ——— ByteBuf
本文是Netty文集中“Netty in action”系列的文章。主要是对Norman Maurer and Marvin Allen Wolfthal 的 《Netty in action》一书简要翻译,同时对重要点加上一些自己补充和扩展。 本章含盖 ByteBuf —— Netty 数据容器 API细节 使用场景 内容分配 如我们之前提到的,网络数据的基本单位是字节。JAVA NIO 提供了 ByteBuffer 作为字节的容器,但是这个类使用过于复杂并且在一些情况下使用过于笨重。 Netty
tomas家的小拨浪鼓
2018/06/27
9150
Netty零拷贝机制
PS:API操作便捷性,动态扩容,多种ByteBuf实现,高效的零拷贝机制(逻辑上边的设计)上边的所有就是nettyByteBuf所做的工作,性能提升,操作性增强。有了理论下节开始实战netty。
IT架构圈
2020/12/16
8750
Netty零拷贝机制
bytebuf池_Netty ByteBuf[通俗易懂]
ByteBuf需要提供JDK ByteBuffer的功能(包含且不限于),主要有以下几类基本功能:
全栈程序员站长
2022/11/04
5360
Java网络编程--Netty中的ByteBuf
由于JDK中提供的ByteBuffer无法动态扩容,并且API使用复杂等原因,Netty中提供了ByteBuf。
CodingDiray
2019/09/25
7570
Java网络编程--Netty中的ByteBuf
Netty源码—6.ByteBuf原理一
答:ByteBuf可以按三个维度来进行分类:一个是堆内和堆外,一个是Unsafe和非Unsafe,一个是Pooled和非Pooled。
东阳马生架构
2025/06/10
1120
推荐阅读
相关推荐
Netty Review - ByteBuf扩容机制源码解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验