文章目录
- 三、Java NIO Buffer: 数据操作的基石
- 3.7 示例代码
- 3.7.1 直接内存Buffer与堆内存Buffer对比
- 3.8 Buffer的底层实现原理
- 3.8.1 Buffer的内存结构
- 3.8.2 直接内存与堆内存的区别
- 3.9 实践与最佳实践
- 3.9.1 Buffer 使用最佳实践
- 3.9.2 高级Buffer技巧
- 3.10 最佳实践与常见陷阱
- 3.10.1 ✅ 推荐实践
- 3.10.2 ⚠️ 常见错误
- 3.11 Buffer 的优缺点总结
- 3.12 总结:Buffer 的核心价值
- 3.13 一句话总结
三、Java NIO Buffer: 数据操作的基石
- Java NIO Buffer: 数据操作的基石【上篇】
3.7 示例代码
3.7.1 直接内存Buffer与堆内存Buffer对比
package cn.tcmeta.bytebuffer;import java.nio.ByteBuffer;public class DirectVsHeapBuffer {public static void main(String[] args) {final int SIZE = 1024 * 1024; final int ITERATIONS = 1000;System.out.println("测试堆内存Buffer...");long heapTime = testHeapBuffer(SIZE, ITERATIONS);System.out.println("测试直接内存Buffer...");long directTime = testDirectBuffer(SIZE, ITERATIONS);System.out.println("\n=== 性能对比 ===");System.out.println("堆内存Buffer耗时: " + heapTime + "ms");System.out.println("直接内存Buffer耗时: " + directTime + "ms");System.out.println("性能差异: " + (heapTime - directTime) + "ms");}private static long testHeapBuffer(int size, int iterations) {long startTime = System.currentTimeMillis();for (int i = 0; i < iterations; i++) {ByteBuffer buffer = ByteBuffer.allocate(size);for (int j = 0; j < size; j++) {buffer.put((byte) (j % 256));}buffer.flip();while (buffer.hasRemaining()) {buffer.get();}if (i % 100 == 0) {System.gc();}}return System.currentTimeMillis() - startTime;}private static long testDirectBuffer(int size, int iterations) {long startTime = System.currentTimeMillis();for (int i = 0; i < iterations; i++) {ByteBuffer buffer = ByteBuffer.allocateDirect(size);for (int j = 0; j < size; j++) {buffer.put((byte) (j % 256));}buffer.flip();while (buffer.hasRemaining()) {buffer.get();}if (i % 100 == 0) {cleanDirectBuffer(buffer);}}return System.currentTimeMillis() - startTime;}private static void cleanDirectBuffer(ByteBuffer buffer) {if (buffer.isDirect()) {try {Object cleaner = buffer.getClass().getMethod("cleaner").invoke(buffer);if (cleaner != null) {cleaner.getClass().getMethod("clean").invoke(cleaner);}} catch (Exception e) {}}}
}
测试堆内存Buffer...
测试直接内存Buffer...=== 性能对比 ===
堆内存Buffer耗时: 1324ms
直接内存Buffer耗时: 1224ms
性能差异: 100ms
3.8 Buffer的底层实现原理
3.8.1 Buffer的内存结构
Buffer 的核心是一个数组和三个位置指针:
public abstract class Buffer {private int position = 0; private int limit; private int capacity; private int mark = -1;final Object array;final int arrayOffset;final long address;
}
class HeapByteBuffer extends ByteBuffer {protected final byte[] hb; protected final int offset;HeapByteBuffer(int cap, int lim) {super(-1, 0, lim, cap, new byte[cap], 0);hb = new byte[cap];offset = 0;}@Overridepublic ByteBuffer put(byte x) {hb[ix(nextPutIndex())] = x; return this;}protected int ix(int i) {return i + offset;}
}
3.8.2 直接内存与堆内存的区别
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {protected long address;protected final Cleaner cleaner;DirectByteBuffer(int cap) {super(-1, 0, cap, cap);address = unsafe.allocateMemory(cap);cleaner = Cleaner.create(this, new Deallocator(address, cap));}private static class Deallocator implements Runnable {private long address;private long size;Deallocator(long address, long size) {this.address = address;this.size = size;}public void run() {if (address != 0) {unsafe.freeMemory(address);address = 0;}}}
}
3.9 实践与最佳实践
3.9.1 Buffer 使用最佳实践
- Buffer池化实践
- 高性能文件读取
- 网络编程中的Buffer使用
- 避免常见的Buffer陷阱
package cn.tcmeta.bytebuffer;import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class BufferBestPractices {public static class BufferPool {private final ByteBuffer[] pool;private final int bufferSize;private int available;public BufferPool(int poolSize, int bufferSize) {this.pool = new ByteBuffer[poolSize];this.bufferSize = bufferSize;this.available = poolSize;for (int i = 0; i < poolSize; i++) {pool[i] = ByteBuffer.allocateDirect(bufferSize);}}public synchronized ByteBuffer acquire() throws InterruptedException {while (available == 0) {wait(); }available--;return pool[available].clear(); }public synchronized void release(ByteBuffer buffer) {if (available < pool.length) {pool[available] = buffer;available++;notify(); }}}public static void readFileWithBuffer(String filePath) throws Exception {try (FileChannel channel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {ByteBuffer buffer = ByteBuffer.allocateDirect(8192); while (channel.read(buffer) != -1) {buffer.flip();processBufferData(buffer);buffer.compact();}buffer.flip();while (buffer.hasRemaining()) {processBufferData(buffer);}}}private static void processBufferData(ByteBuffer buffer) {byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("处理数据: " + new String(data).trim());}public static class NetworkBufferHandler {private ByteBuffer readBuffer;private ByteBuffer writeBuffer;public NetworkBufferHandler(int bufferSize) {readBuffer = ByteBuffer.allocate(bufferSize);writeBuffer = ByteBuffer.allocate(bufferSize);}public void handleRead() {readBuffer.flip();try {while (readBuffer.hasRemaining()) {byte b = readBuffer.get();processByte(b);}} finally {readBuffer.compact(); }}public void prepareWrite(byte[] data) {writeBuffer.clear();writeBuffer.put(data);writeBuffer.flip();}private void processByte(byte b) {}}public static void avoidCommonPitfalls() {ByteBuffer buffer = ByteBuffer.allocate(100);buffer.put("Hello".getBytes());buffer.put("Hello".getBytes());buffer.flip(); buffer.clear(); buffer.compact(); ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);}public static void main(String[] args) {System.out.println("Buffer 最佳实践示例");BufferPool pool = new BufferPool(10, 4096);try {ByteBuffer buffer = pool.acquire();try {buffer.put("测试数据".getBytes());buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("处理的数据: " + new String(data));} finally {pool.release(buffer);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}
3.9.2 高级Buffer技巧
- 视图Buffer, 在不同的数据类型间转换
- 字节序处理
- 批量操作
- 只读Buffer
package cn.tcmeta.bytebuffer;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;public class AdvancedBufferTechniques {public static void demonstrateViewBuffers() {System.out.println("=== 视图Buffer示例 ===");ByteBuffer byteBuffer = ByteBuffer.allocate(32);byteBuffer.putInt(123); byteBuffer.putDouble(45.67); byteBuffer.putChar('A'); byteBuffer.flip();IntBuffer intBuffer = byteBuffer.asIntBuffer();DoubleBuffer doubleBuffer = byteBuffer.asDoubleBuffer();CharBuffer charBuffer = byteBuffer.asCharBuffer();System.out.println("Int视图: " + intBuffer.get());System.out.println("Double视图: " + doubleBuffer.get());System.out.println("Char视图: " + charBuffer.get());}public static void demonstrateByteOrder() {System.out.println("\n=== 字节序示例 ===");ByteBuffer buffer = ByteBuffer.allocate(4);System.out.println("默认字节序: " + buffer.order());buffer.order(ByteOrder.LITTLE_ENDIAN);buffer.putInt(0x12345678);buffer.flip();for (int i = 0; i < 4; i++) {System.out.printf("%02X ", buffer.get());}System.out.println();}public static void demonstrateBulkOperations() {System.out.println("\n=== 批量操作示例 ===");ByteBuffer buffer = ByteBuffer.allocate(100);byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};buffer.put(data); buffer.flip();byte[] result = new byte[buffer.remaining()];buffer.get(result); System.out.print("批量获取的数据: ");for (byte b : result) {System.out.print(b + " ");}System.out.println();}public static void demonstrateReadOnlyBuffer() {System.out.println("\n=== 只读Buffer示例 ===");ByteBuffer original = ByteBuffer.allocate(10);original.put("Hello".getBytes());original.flip();ByteBuffer readOnly = original.asReadOnlyBuffer();System.out.print("只读Buffer内容: ");while (readOnly.hasRemaining()) {System.out.print((char) readOnly.get());}System.out.println();try {readOnly.put((byte) 'X');} catch (Exception e) {System.out.println("正确捕获异常: " + e.getClass().getSimpleName());}}public static void demonstrateMemoryMappedBuffer() throws Exception {System.out.println("\n=== 内存映射Buffer示例 ===");System.out.println("内存映射文件提供了一种将文件直接映射到内存的机制,");System.out.println("可以通过MappedByteBuffer直接操作文件内容,");System.out.println("避免了系统调用的开销,极大提高了IO性能。");}public static void main(String[] args) throws Exception {demonstrateViewBuffers();demonstrateByteOrder();demonstrateBulkOperations();demonstrateReadOnlyBuffer();demonstrateMemoryMappedBuffer();}
}
3.10 最佳实践与常见陷阱
3.10.1 ✅ 推荐实践
实践 | 说明 |
---|
优先使用 DirectBuffer 进行 I/O | 减少内存拷贝 |
合理设置 Buffer 大小 | 避免过小(频繁 flip)或过大(内存浪费) |
使用 hasRemaining() 判断是否可读写 | 安全读写 |
及时调用 flip() 切换模式 | 核心操作,不可遗漏 |
使用 try-finally 释放 DirectBuffer | 防止内存泄漏 |
3.10.2 ⚠️ 常见错误
buffer.put("data".getBytes());
buffer.put("data".getBytes());
buffer.flip();
channel.write(buffer);
3.11 Buffer 的优缺点总结
3.11.1 ✅ 优点
优点 | 说明 |
---|
高性能 | 块式读写,减少系统调用 |
内存控制 | 明确的容量与边界 |
零拷贝支持 | DirectBuffer 和 MappedByteBuffer |
线程安全 | 单线程操作,无并发问题(通常) |
类型丰富 | 支持多种基本类型 |
3.11.2 ❌ 缺点
缺点 | 说明 |
---|
状态管理复杂 | position /limit /flip() 易出错 |
API 繁琐 | 相比流式 IO 更复杂 |
堆外内存泄漏风险 | DirectBuffer 不受 GC 直接管理 |
学习成本高 | 需理解读写模式切换 |
3.12 总结:Buffer 的核心价值
维度 | 说明 |
---|
核心角色 | NIO 的“数据容器” |
设计思想 | 块式 I/O 取代流式 I/O |
核心能力 | 统一的数据读写接口,支持高效 I/O |
适用场景 | 文件读写、网络通信、大文件处理 |
NIO 地位 | 与 Channel 、Selector 并列为三大基石 |
3.13 一句话总结
Buffer
是 Java NIO 的“心脏”
- 它通过容量、位置、上限、标记四要素精确控制数据流,结合
flip()
等操作实现高效的读写切换 - 为高性能 I/O 提供了坚实基础。掌握
Buffer
,是掌握 NIO 的第一步。