【JAVA之Buffer介绍】

Java NIO缓冲区详解
本文介绍了Java NIO中的缓冲区(Buffer)概念及其优势,详细解释了ByteBuffer的使用方法及特性,包括创建、读写、回绕等操作,并对比了直接缓冲区与非直接缓冲区的优缺点。

一、Buffer介绍

缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区:

 

二、Buffer好处

使用缓冲区有这么两个好处:

1、减少实际的物理读写次数

2、缓冲区在创建时就被分配内存,这块内存区域一直被重用,可以减少动态分配和回收内存的次数

 

三、ByteBuffer字节缓冲区。

此类定义了除 boolean 之外,读写所有其他基本类型值的方法。这些基本值可以根据缓冲区的当前字节顺序与字节序列互相进行转换,并可以通过 order 方法获取和修改。特定的字节顺序由 ByteOrder 类的实例表示。字节缓冲区的初始顺序始终是 BIG_ENDIAN。

此类针对字节缓冲区定义了以下六类操作:

1)读写单个字节的绝对和相对 get 和 put 方法;

2)将此缓冲区的连续字节序列传输到数组中的相对批量 get 方法;

3)将 byte 数组或其他字节缓冲区中的连续字节序列传输到此缓冲区的相对批量 put 方法;

4)读写其他基本类型值,并按照特定的字节顺序在字节序列之间转换这些值的绝对和相对 get 和 put 方法;

5)创建视图缓冲区 的方法,这些方法允许将字节缓冲区视为包含其他基本类型值的缓冲区;

6)对字节缓冲区进行 compacting、duplicating 和 slicing 的方法。

字节缓冲区可以通过 allocation 方法创建,此方法为该缓冲区的内容分配空间,或通过 wrapping 方法将现有的 byte 数组包装到缓冲区中来创建。

 

四、直接与直接缓冲区

字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。

直接字节缓冲区可以通过调用此类的 allocateDirect 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。

直接字节缓冲区还可以通过 mapping 将文件区域直接映射到内存中来创建。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。

字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect 方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。

 

 

五、缓冲区使用步骤

在NIO中,数据的读写操作始终是与缓冲区相关联的.读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入缓冲区.缓冲区是定长的,基本上它只是一个列表,它的所有元素都是基本数据类型.ByteBuffer是最常用的缓冲区,它提供了读写其他数据类型的方法,且信道的读写方法只接收ByteBuffer.因此ByteBuffer的用法是有必要牢固掌握的.

 

1.创建ByteBuffer

1.1 使用allocate()静态方法

    ByteBuffer buffer=ByteBuffer.allocate(256);

    以上方法将创建一个容量为256字节的ByteBuffer,如果发现创建的缓冲区容量太小,唯一的选择就是重新创建一个大小合适的缓冲区.

 

1.2 通过包装一个已有的数组来创建

    如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.

    ByteBuffer buffer=ByteBuffer.wrap(byteArray);

 

    如果要将一个字符串存入ByteBuffer,可以如下操作:

    String sendString="你好,服务器. ";

    ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-16"));

 

2.回绕缓冲区

  buffer.flip();

  这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取.

 

3.清除缓冲区

  buffer.clear();

  这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区.

 

4.从套接字通道(信道)读取数据

  int bytesReaded=socketChannel.read(buffer);

  执行以上方法后,通道会从socket读取的数据填充此缓冲区,它返回成功读取并存储在缓冲区的字节数.在默认情况下,这至少会读取一个字节,或者返回-1指示数据结束.

 

5.向套接字通道(信道)写入数据

  socketChannel.write(buffer);

  此方法以一个ByteBuffer为参数,试图将该缓冲区中剩余的字节写入信道.

 

 

 

缓冲区(Buffer)其实他有两个子类:一个是HeapByteBuffer和DirectByteBuffer关于这两个类的区别很好理解:

DirectByteBuffer不是分配在堆上的,它不被GC直接管理(但Direct Buffer的JAVA对象是归GC管理的,只要GC回收了它的JAVA对象,操作系统才会释放Direct Buffer所申请的空间),它似乎给人感觉是“内核缓冲区(buffer in kernel)”。HeapByteBuffer则是分配在堆上的,或者我们可以简单理解为Heap Buffer就是byte[]数组的一种封装形式,查看JAVA源代码实现,HeapByteBuffer也的确是这样。 说白了就是HeapByteBuffer是在JVM堆内存中分配会被JVM管理回收,但是DirectByteBuffer是直接由系统内存进行分配,不被JVM管理。通过上面的区别看到:

1、创建和释放DirectByteBuffer的代价比HeapByteBuffer得要高,因为JVM堆中分配和释放内存肯定比系统分配和创建内存高效

2、因为平时的read/write,都会在I/O设备与应用程序空间之间经历一个“内核缓冲区”。 DirectByteBuffer就好比是“内核缓冲区”上的缓存,不直接受GC管理;而Heap Buffer就仅仅是byte[]字节数组的包装形式。因此把一个Direct Buffer写入一个Channel的速度要比把一个HeapByteBuffer写入一个Channel的速度要快。 

所以这两个类操作起来各有好处,要视情况而定,一般如果是一个ByteBuffer经常被重用的话,就可以使用DirectByteBuffer对象。如果是需要经常释放和分配的地方用HeapByteBuffer对象。

<think>我们在讨论Java中的DataBuffer。用户希望了解DataBuffer的用法和实现。在Java标准库中,有Buffer类(在java.nio包中),但没有名为DataBuffer的类。不过,可能存在几种情况:1.用户可能指的是java.nio.Buffer及其子类(如ByteBuffer,CharBuffer等)。2.用户可能指的是Java中其他包中的类似概念,比如在图像处理中java.awt.image.DataBuffer。3.或者用户指的是某种特定框架或库中的DataBuffer(例如Netty中的DataBuffer,Spring框架中的DataBuffer等)。根据用户提供的引用内容,里面提到了java.nio.Buffer及其子类(如CharBuffer),也提到了buffer-slayer这样的工具(用于批量处理请求)。另外,在引用[3]中提到了并发编程,引用[4]提到批量操作,引用[5]提到邮件配置。但是,用户明确提到了“JavaDataBuffer”,我们需要判断最可能的含义。首先,在Java标准库中,有两个常见的可能性:1.java.nio.Buffer:这是NIO中用于缓冲数据的基类。2.java.awt.image.DataBuffer:这是用于图像数据的缓冲区。另外,在Spring框架中(特别是SpringWebFlux)有一个org.springframework.core.io.buffer.DataBuffer接口。由于用户没有具体上下文,而且我们之前讨论的内容包括NIO(引用[1][2])和批量操作(引用[4]),可能用户指的是NIO中的Buffer。但是,在NIO中并没有一个名为DataBuffer的类,所以可能是笔误,或者是想了解如何用NIO的Buffer处理数据。还有一种可能是用户希望处理二进制数据,而NIO中的ByteBuffer就是用来处理字节数据的。因此,我考虑从以下几个方面回答:1.如果用户指的是NIO中的Buffer,那么我们可以介绍java.nio.Buffer及其子类(如ByteBuffer)的用法和实现。2.如果用户指的是图像处理中的DataBuffer,那么介绍java.awt.image.DataBuffer。3.如果用户指的是Spring框架中的DataBuffer,那么需要说明这是Spring的类。由于用户提供的引用中大量提到NIO,并且没有涉及图像或SpringWebFlux,因此我们优先考虑NIOBuffer。但是,用户明确写了“DataBuffer”,所以我们需要澄清。为了准确起见,我应该在回答开头先澄清:Java标准库中没有名为DataBuffer的类,但存在类似的名称,比如:-java.nio.Buffer:NIO中用于数据缓冲的基类。-java.awt.image.DataBuffer:用于图像数据存储。此外,在第三方库(如Spring)中也有DataBuffer。根据用户提供的引用,大部分是关于NIO的(引用[1]和[2]),所以我将重点放在NIOBuffer上,但也会提及其他可能性。因此,回答结构:1.澄清DataBuffer名称,并指出可能的类。2.重点讲解NIOBuffer(因为引用中出现了NIO)的用法和实现。3.简要说明其他可能的DataBuffer(图像处理中的DataBuffer和Spring中的DataBuffer),以覆盖用户可能的需求。4.生成相关问题。注意:用户没有提到图像处理也没有提到Spring,所以后两部分可以简要描述。具体内容:一、关于DataBuffer名称的澄清二、JavaNIOBuffer1.简介2.类型:列出各种Buffer(ByteBuffer,CharBuffer等)[^1]3.分配Buffer:如CharBuffer.allocate(1024)[^2]4.读写数据:put和get方法5.其他操作:flip,clear,compact等6.内存管理:直接缓冲区和非直接缓冲区三、图像处理中的DataBufferjava.awt.image.DataBuffer)简要说明:用于存储图像像素数据,有DataBufferByte、DataBufferInt等子类。四、Spring框架中的DataBuffer(org.springframework.core.io.buffer.DataBuffer)简要说明:用于非阻塞式I/O操作,通常在网络编程中使用,如WebFlux。由于引用[4]提到了批量操作,所以在NIO部分可以提一下使用Buffer进行批量传输(如通道的读写)有助于提高效率。代码示例:可以给一个使用ByteBuffer读写文件的例子。最后,生成相关问题。注意:在回答中,我们引用了用户提供的引用[1][2],所以加上引用标识[^1][^2]。同时,对于我们提及的其他内容(比如图像处理中的DataBuffer)不需要加引用标识,因为用户没有提供相关引用。下面开始编写回答:</think> ### Java中DataBuffer的用法与实现 #### 🔍 名称澄清 Java标准库中不存在**`DataBuffer`**类,可能指以下三类相似组件: 1. **`java.nio.Buffer`及其子类**(如`ByteBuffer`、`CharBuffer`等) 2. **`java.awt.image.DataBuffer`**(图像处理专用) 3. **第三方库的DataBuffer**(如Spring框架中的`org.springframework.core.io.buffer.DataBuffer`)。 根据您提供的引用内容(多涉及NIO和并发编程),我们将重点解析**NIO Buffer**的实现与用法。 --- ### 🛠 一、NIO Buffer的核心机制 `java.nio.Buffer`是NIO中**数据容器**的基类,支持高效I/O操作。其子类覆盖常见数据类型(引用[^1]): - `ByteBuffer`, `CharBuffer`, `IntBuffer`, `DoubleBuffer`等。 #### 📍 核心属性 ```java // Buffer的内部状态 private int capacity; // 缓冲区总容量(固定) private int position; // 当前读写位置索引 private int limit; // 可操作数据上限 private int mark = -1; // 标记位置(用于reset) ``` ![](https://www.baeldung.com/wp-content/uploads/2018/08/byte-buffer.png) *▲ Buffer状态示意图(来源:Baeldung)* #### ⚙️ 关键操作 1. **分配缓冲区**(分配堆内存或直接内存): ```java // 堆内分配(JVM管理) CharBuffer buf = CharBuffer.allocate(1024); // 引用[^2] // 直接内存分配(减少JVM堆拷贝) ByteBuffer directBuf = ByteBuffer.allocateDirect(1024); ``` 2. **数据写入**: ```java buf.put('A'); // 单个写入 buf.put("Hello".toCharArray()); // 批量写入 ``` 3. **读写切换**: ```java buf.flip(); // 切换为读模式:limit=position, position=0 while(buf.hasRemaining()) { System.out.print(buf.get()); // 顺序读取 } ``` 4. **重置状态**: ```java buf.clear(); // 重置position=0, limit=capacity(可重写) buf.compact(); // 移动未读数据至头部(部分读取后继续写入) ``` --- ### 🔄 二、并发场景的使用要点 NIO Buffer**非线程安全**,多线程使用时需同步(引用[^3]): - 解决方案1:使用`synchronized`或`ReentrantLock` ```java synchronized(buffer) { buffer.put(data); } ``` - 解决方案2:通过线程池隔离(如`ExecutorService`) ▶️ **性能技巧**:批量操作时优先用`put(byte[] src)`,比单字节操作快5-10倍(JVM内联优化)。 --- ### 🖼 三、其他DataBuffer类型 1. **`java.awt.image.DataBuffer`** - 用途:存储图像像素数据(如RGB值) - 子类:`DataBufferByte`, `DataBufferInt`等 ```java BufferedImage image = new BufferedImage(width, height, TYPE_INT_RGB); DataBufferInt buffer = (DataBufferInt) image.getRaster().getDataBuffer(); int[] pixels = buffer.getData(); // 直接操作像素数组 ``` 2. **Spring框架的DataBuffer** - 核心接口:`org.springframework.core.io.buffer.DataBuffer` - 场景:WebFlux非阻塞I/O、网络流处理 ```java DataBufferFactory factory = new DefaultDataBufferFactory(); DataBuffer buffer = factory.allocateBuffer(256); buffer.write("Spring Data".getBytes()); ``` --- ### 💡 典型应用场景 1. **文件高效读写**:通过`FileChannel + ByteBuffer`减少系统调用次数 2. **网络通信**:结合`SocketChannel`处理TCP数据包 3. **批量操作优化**:如Redis Pipeline、JDBC Batch(引用[^4]) 4. **图像处理**:修改像素数据后生成新图片 --- 📌 **注意事项** - 直接内存`allocateDirect()`需手动管理,避免`OutOfMemoryError` - 部分实现(如邮件协议配置)依赖底层协议参数(引用[^5]) - 图像`DataBuffer`修改后需调用`Raster.setDataBuffer()`生效 --- ### 🔗 相关问题 1. NIO直接内存缓冲区与堆内存缓冲区的性能差异如何量化? 2. 如何在Spring WebFlux中自定义DataBuffer的分配策略? 3. `ByteBuffer`如何处理TCP粘包/半包问题? 4. Java图像处理中`DataBuffer`与`BufferedImage`的协作机制是什么? [^1]: Java NIO Buffer Type Docs [^2]: NIO Buffer Allocation Example [^3]: Java并发编程的原子性保证 [^4]: 批量操作优化工具buffer-slayer的用途 [^5]: JavaMail协议配置的优先级规则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值