当前位置: 首页 > news >正文

Netty从0到1系列之Buffer【上】

文章目录

  • 三、Java NIO Buffer: 数据操作的基石
    • 3.1 Buffer 是什么?
    • 3.2 Buffer 的继承体系与类型
    • 3.3 Buffer 的核心属性与状态机制
    • 3.4 Buffer的工作模式: 读写切换
      • 3.4.1 ✅ 写模式(Write Mode)
      • 3.4.2 ✅ 读模式(Read Mode)
      • 3.4.3 模式切换
    • 3.5 Buffer的核心API
      • 3.5.1 ByteBuffer对象的创建
      • 3.5.2 ByteBuffer原理解析
      • 3.5.3 内存分配

三、Java NIO Buffer: 数据操作的基石

3.1 Buffer 是什么?

Buffer 是 Java NIO 中的核心组件,用于与 NIO Channel 进行数据交互。它是一个特定基本类型数据的容器,本质上是一个可以写入数据、读取数据的内存块(通常是数组),提供了结构化访问数据的方法和机制。

Buffer 的核心特性:

  • 容量固定:创建时指定容量,不可改变
  • 位置控制:通过 position、limit、capacity 控制读写位置
  • 双向操作:支持读写切换,通过 flip() 方法实现
  • 内存高效:支持堆内存和直接内存两种实现
  • 类型特定:为各种基本类型提供了相应的 Buffer 实现
Buffer内部
capacity: 容量
position: 位置
limit: 上限
mark: 标记
应用程序
Buffer
Channel
文件/网络

🌟 核心定位

  • Buffer 是一个固定大小的数据容器
  • 所有 I/O 操作必须通过 Buffer 进行
  • 它维护一组状态变量,控制数据的读写边界

3.2 Buffer 的继承体系与类型

Java NIO 为各种基本数据类型提供了相应的 Buffer 实现:

public abstract class Buffer {// Invariants: mark <= position <= limit <= capacityprivate int mark = -1;private int position = 0;private int limit;private int capacity;// ...
}
Buffer-> ByteBuffer (最常用)-> MappedByteBuffer (内存映射文件)-> CharBuffer-> ShortBuffer-> IntBuffer-> LongBuffer-> FloatBuffer-> DoubleBuffer

在这里插入图片描述

重要的实现类ByteBuffer

public abstract class ByteBufferextends Bufferimplements Comparable<ByteBuffer>

在这里插入图片描述

3.3 Buffer 的核心属性与状态机制

Buffer 通过三个核心属性来控制数据的读写位置和边界:

新创建或clear()后
flip()操作
clear()或compact()操作
WriteMode
position=0
limit=capacity
capacity=固定值
WPosition
WLimit
WCapacity
ReadMode
position=0
limit=原position
capacity=固定值
RPosition
RLimit
RCapacity
写模式下:
position: 下一个要写入的位置
limit: 可以写入的最大位置
capacity: 缓冲区总容量
读模式下:
position: 下一个要读取的位置
limit: 可以读取的最大位置
capacity: 缓冲区总容量

每个 Buffer 都有四个关键属性,它们共同管理数据的读写过程:

属性类型说明
capacityint容量,Buffer 最多能容纳的数据量(不可变)
positionint位置,下一个要读或写的索引(0 ≤ position ≤ limit)
limitint上限,可读/写数据的边界(0 ≤ limit ≤ capacity)
markint标记,可记录某个位置,后续可通过 reset() 回到该位置

🌟 核心规则

  • 0 ≤ mark ≤ position ≤ limit ≤ capacity
  • 调用 reset() 可将 position 回到 mark 位置
  • mark 初始为 -1,表示未设置

3.4 Buffer的工作模式: 读写切换

Buffer 有两种工作模式: 读模式和写模式.

3.4.1 ✅ 写模式(Write Mode)

  • 应用程序向 Buffer 写入数据
  • position 指向下一个可写位置
  • limit 通常等于 capacity
写模式
4
3
2
1
0
position
limit
capacity

3.4.2 ✅ 读模式(Read Mode)

  • 从 Buffer 读取数据到应用程序
  • position 指向下一个可读位置
  • limit 指向可读数据末尾
读模式
4
3
2
1
0
position
limit
capacity

3.4.3 模式切换

🌟 模式切换:通过 flip() 方法完成写 → 读的切换

3.5 Buffer的核心API

这里以ByteBuffer为例进行说明.

3.5.1 ByteBuffer对象的创建

// 分配新的直接字节缓冲区。
// 新缓冲区的位置将为零,其限制将是其容量,其标记将未定义,其每个元素将初始化为零,其字节顺序将为 BIG_ENDIAN。它是否有 a backing array 尚未指定。
// 参数:
// 		capacity – 新缓冲区的容量(以字节为单位)
// 返回:
// 		新字节缓冲区
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}// 分配新的字节缓冲区。
// 新缓冲区的位置将为零,其限制将是其容量,其标记将未定义,其每个元素将初始化为零,其字节顺序将为 BIG_ENDIAN。它将有一个 backing array,并且它 array offset 将为零。
// 参数:
// 		capacity – 新缓冲区的容量(以字节为单位)
// 返回:
// 		新字节缓冲区
// 抛出:
// 		IllegalArgumentException – 如果 是 capacity 负整数
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw createCapacityException(capacity);return new HeapByteBuffer(capacity, capacity, null);
}// 将字节数组包装到缓冲区中。
// 新缓冲区将由给定的字节数组支持;也就是说,对缓冲区的修改将导致数组被修改,反之亦然。新缓冲区的容量将为 array.length,其位置将为 offset,其限制将为 offset + length,其标记将未定义,其字节顺序将为 BIG_ENDIAN。它 backing array 将是给定的数组,并且为 array offset 零。
// 参数:
// 		array – 将支持新缓冲区的数组
// 		offset– 要使用的子数组的偏移量;必须为非负数且不大于 array.length。新缓冲区的位置将设置为此值。
// 		length– 要使用的子数组的长度;必须为非负数且不大于 array.length - offset。新缓冲区的限制将设置为 offset + length。
// 返回:
// 		新字节缓冲区
// 抛出:
I// 	ndexOutOfBoundsException– 如果 和 length 参数上的offset先决条件不成立
public static ByteBuffer wrap(byte[] array,int offset, int length)
{try {return new HeapByteBuffer(array, offset, length, null);} catch (IllegalArgumentException x) {throw new IndexOutOfBoundsException();}
}// 将字节数组包装到缓冲区中。
// 新缓冲区将由给定的字节数组支持;也就是说,对缓冲区的修改将导致数组被修改,反之亦然。新缓冲区的容量和限制将为 array.length,其位置为零,其标记将未定义,其字节顺序将为 BIG_ENDIAN。它 backing array 将是给定的数组,并且为 array offset 零。
// 参数:
//		array – 将支持此缓冲区的数组
// 返回:
//		新字节缓冲区
public static ByteBuffer wrap(byte[] array) {return wrap(array, 0, array.length);
}

在这里插入图片描述

3.5.2 ByteBuffer原理解析

四重要的属性:

private int mark = -1;
private int position = 0; // 写入位置
private int limit; // 写入或者读取的限制
private int capacity; // 容量大小

读取操作, 必须调用flip()方法

public Buffer flip() {limit = position; // 将limit赋值到当前position位置position = 0; // position归0mark = -1;return this;
}

在这里插入图片描述

读取4个字节之后, postion位置移动到limit位置.

final int nextGetIndex() {                          // package-privateint p = position;if (p >= limit)throw new BufferUnderflowException();position = p + 1; // 每读取一个字节, position位置往后加1.return p;
}

在这里插入图片描述

如果想继续将channel读取的数据存储到ByteBuffer当中, 必须调用: clear()方法.

在这里插入图片描述

compact方法, 把未读完的部分向前压缩,然后切换为: 【写模式】.

public MappedByteBuffer compact() {int pos = position();int lim = limit();assert (pos <= lim);int rem = (pos <= lim ? lim - pos : 0);try {// null is passed as destination Scope to avoid checking scope() twiceSCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null,ix(pos), null, ix(0), (long)rem << 0);} finally {Reference.reachabilityFence(this);}position(rem);limit(capacity());discardMark();return this;
}

在这里插入图片描述

3.5.3 内存分配

直接内存和堆内存:

  • 堆内存, 读写效率低, 受到JVM的限制.
  • 直接内存,读写效率高(少拷贝一次数据), 大小不受JVM限制.有受垃圾回收的影响.分配内存效率低一些.

Buffer 有两种内存分配方式:

  1. 堆内存 (Heap Buffer):通过allocate(int capacity)创建,基于 JVM 堆内存
  2. 直接内存 (Direct Buffer):通过allocateDirect(int capacity)创建,基于操作系统的本地内存
@Test
public void allocateTest(){ByteBuffer buff1 = ByteBuffer.allocate(10); // 在jvm堆内存分配固定10字节的空间ByteBuffer buff2 = ByteBuffer.allocateDirect(10); // 在直接内存上分配固定10字节的空间log.info("buff1: 对象的名称是: {}", buff1.getClass());log.info("buff2: 对象的名称是: {}", buff2.getClass());
}
19:57:16.949 [main] INFO cn.tcmeta.ByteBufferDemo02 -- buff1: 对象的名称是: class java.nio.HeapByteBuffer
19:57:16.953 [main] INFO cn.tcmeta.ByteBufferDemo02 -- buff2: 对象的名称是: class java.nio.DirectByteBuffer
Buffer
Heap Buffer
- 位于JVM堆中
- 由GC管理
- 可能有额外拷贝
Direct Buffer
- 位于堆外
- 不受GC直接管理
- IO操作性能好
- 分配和释放成本高
类型创建方式说明
堆内 BufferByteBuffer.allocate(100)数据在 JVM 堆中,受 GC 管理
堆外 BufferByteBuffer.allocateDirect(100)数据在 native memory,减少系统调用拷贝

🌟 性能对比

  • 堆外 Buffer:I/O 性能更高(零拷贝)
  • 堆内 Buffer:创建/回收更快,适合小数据
  1. 堆内存特点
    • 垃圾回收器管理:堆内存中的对象由 Java 垃圾回收器管理。这意味着当没有引用指向这个ByteBuffer时,垃圾回收器会在适当的时候回收它所占用的内存空间。
    • 方便性:在编程中使用相对方便,因为它是 Java 对象,可以直接在 Java 代码中进行操作和管理。
    • 可能的性能开销:由于垃圾回收的不确定性,可能会在某些情况下导致性能开销。特别是在频繁进行内存分配和回收的场景下,垃圾回收可能会暂停应用程序的执行,从而影响性能。
  2. 直接内存特点
    • 不受垃圾回收影响:直接内存不由 Java 垃圾回收器管理,因此在某些情况下可以避免垃圾回收带来的性能开销。
    • 适合特定场景:对于一些需要大量内存、频繁进行 I/O 操作(如网络通信和文件读写)的场景,直接内存可能会提供更好的性能。这是因为直接内存可以与本机 I/O 操作更好地交互,减少数据在 Java 堆内存和本机内存之间的复制次数。
    • 内存管理复杂性:使用直接内存需要手动管理内存,并且在使用不当的情况下可能会导致内存泄漏。例如,如果忘记释放不再使用的直接内存,可能会导致本机内存耗尽。
    • 可能的内存溢出问题:直接内存的分配不受 Java 堆大小的限制,但它仍然受到本机内存总量的限制。如果分配过多的直接内存,可能会导致本机内存不足,从而引发OutOfMemoryError错误。

如何选择直接内存和堆内存?

  • 性能需求:如果应用程序对性能要求较高,特别是在频繁进行 I/O 操作的情况下,可以考虑使用直接内存。但是,需要进行性能测试以确定直接内存是否确实能提高性能,因为在某些情况下,堆内存的性能可能更好。
  • 内存管理:使用直接内存需要更加小心地管理内存,确保及时释放不再使用的内存。否则,可能会导致内存泄漏和本机内存耗尽。
  • 应用场景:对于一些需要与本机代码进行交互或者需要大量内存的场景,直接内存可能更合适。而对于一般的应用程序,如果没有特殊的性能需求,堆内存可能是更简单和安全的选择


文章转载自:

http://aukvlPhc.jwxmn.cn
http://7N1vrdtj.jwxmn.cn
http://vXxDb7tm.jwxmn.cn
http://UUd3621t.jwxmn.cn
http://7J3Nt14k.jwxmn.cn
http://YQvBgm54.jwxmn.cn
http://U6EdbyDi.jwxmn.cn
http://mpNjaKnw.jwxmn.cn
http://AsYZYXTa.jwxmn.cn
http://rdOsoeCT.jwxmn.cn
http://FSA8qvhG.jwxmn.cn
http://nFbKnpUo.jwxmn.cn
http://8FfAJ1MY.jwxmn.cn
http://4f3F6GrE.jwxmn.cn
http://eyvLkJPF.jwxmn.cn
http://K1ymvbSF.jwxmn.cn
http://0R6nnMwC.jwxmn.cn
http://P8q632J5.jwxmn.cn
http://y8X5HuHK.jwxmn.cn
http://sN3Sb5um.jwxmn.cn
http://kkdP8U20.jwxmn.cn
http://ssep7pot.jwxmn.cn
http://STdVLwyA.jwxmn.cn
http://sYPxDbPR.jwxmn.cn
http://cxZS3JXr.jwxmn.cn
http://cSXiPx71.jwxmn.cn
http://Utyvtt9X.jwxmn.cn
http://QFtWL2nd.jwxmn.cn
http://hI0iEWqE.jwxmn.cn
http://Zr5OtLsQ.jwxmn.cn
http://www.dtcms.com/a/367515.html

相关文章:

  • Flutter之riverpod状态管理Widget UI详解
  • 投标委托测试如何选择第三方检测机构?
  • 记录SSL部署,链路不完整问题
  • Unity Standard Shader 解析(五)之ShadowCaster
  • go 初始化组件最佳实践
  • 2025数学建模国赛高教社杯A题思路代码文章助攻
  • deveco 出现hvigor版本与系统版本不匹配
  • (自用)Linux 常用命令自查文档
  • QT6 配置 Copilot插件
  • 以StarRocks为例讲解MPP架构和列式存储
  • Kafka 学习教程:从基础概念到实践操作
  • 香港云主机常见使用问题汇总
  • 【图像处理基石】图像在频域处理和增强时,如何避免频谱混叠?
  • 【C++】17. AVL树实现
  • Java基础 9.4
  • 市政管网,各种规格的管件汇总大全
  • 【数据模型】思维导图的数据结构模型
  • 力扣字符串刷题-六道题记录-1
  • 【研究前沿】【书读多了,自然就聪明】人工智能中出现的智能涌现的原理是什么?为什么大模型能产生智能?能够泛化?深入了解背后的机制
  • ConvertAPI:PDF转Word的便捷之选
  • 正运动控制卡学习-点动
  • CodeBuddy+Lucene 探索与实践日志:记录我如何从零构建桌面搜索引擎
  • 虚拟化安全:从逃逸漏洞到实战分析
  • 实战演练(二):结合路由与状态管理,构建一个小型博客前台
  • Webus 与中国国际航空合作实现 XRP 支付
  • 专项智能练习(计算机动画基础)
  • webpack scope hositing 和tree shaking
  • AGX Orin平台RTC驱动导致reboot系统卡住问题调试
  • 期权平仓后权利金去哪了?
  • 基于深度掩码的动态模糊处理