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

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

文章目录

  • 三、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.11.1 ✅ 优点
      • 3.11.2 ❌ 缺点
    • 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; // 1MBfinal 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++) {// 创建堆内存BufferByteBuffer buffer = ByteBuffer.allocate(size);// 写入数据for (int j = 0; j < size; j++) {buffer.put((byte) (j % 256));}// 读取数据buffer.flip();while (buffer.hasRemaining()) {buffer.get();}// 等待GC,模拟真实环境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++) {// 创建直接内存BufferByteBuffer 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 {// 使用反射调用Cleaner的clean方法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 的核心是一个数组和三个位置指针:

// Buffer 的简化内部结构
public abstract class Buffer {// 三个核心属性private int position = 0;    // 下一个要读写的位置private int limit;           // 可以读写的位置上限private int capacity;        // 缓冲区总容量// 标记位置,用于reset()操作private int mark = -1;// 包装的数组(对于堆缓冲区)final Object array;final int arrayOffset;// 地址(对于直接缓冲区)final long address;
}// ByteBuffer 的堆内存实现
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;  // 通过ix计算索引return this;}// 索引计算方法protected int ix(int i) {return i + offset;}
}

3.8.2 直接内存与堆内存的区别

// 直接内存Buffer的实现原理
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 {// 1. 🚀🚀🚀🚀🚀 Buffer 池化实践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;// 初始化Buffer池for (int i = 0; i < poolSize; i++) {pool[i] = ByteBuffer.allocateDirect(bufferSize);}}public synchronized ByteBuffer acquire() throws InterruptedException {while (available == 0) {wait(); // 等待可用Buffer}available--;return pool[available].clear(); // 返回并清空Buffer}public synchronized void release(ByteBuffer buffer) {if (available < pool.length) {pool[available] = buffer;available++;notify(); // 通知等待的线程}}}// 2. 🐦‍🔥🐦‍🔥🐦‍🔥🐦‍🔥🐦‍🔥🐦‍🔥 高性能文件读取public static void readFileWithBuffer(String filePath) throws Exception {try (FileChannel channel = FileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {// 使用直接内存Buffer提高IO性能ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // 8KB缓冲区while (channel.read(buffer) != -1) {// 切换为读模式buffer.flip();// 处理数据processBufferData(buffer);// 清空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());}// 3. 🍁🍁🍁🍁🍁🍁🍁 网络编程中的Buffer使用public static class NetworkBufferHandler {private ByteBuffer readBuffer;private ByteBuffer writeBuffer;public NetworkBufferHandler(int bufferSize) {// 为读写分别分配BufferreadBuffer = 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) {// 处理字节数据}}// 4. ✅✅✅✅✅✅✅ 避免常见的Buffer陷阱public static void avoidCommonPitfalls() {// 陷阱1:忘记调用flip()ByteBuffer buffer = ByteBuffer.allocate(100);buffer.put("Hello".getBytes());// 忘记调用 buffer.flip() 会导致读取不到数据// 正确做法buffer.put("Hello".getBytes());buffer.flip(); // 切换为读模式// 陷阱2:错误使用clear()和compact()buffer.clear(); // 清空整个Bufferbuffer.compact(); // 只清空已处理的数据,保留未处理数据// 陷阱3:直接内存泄漏ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);// 使用后需要适当清理(通常通过GC,但大缓冲区建议手动管理)}public static void main(String[] args) {System.out.println("Buffer 最佳实践示例");// 创建Buffer池BufferPool pool = new BufferPool(10, 4096);try {// 从池中获取BufferByteBuffer buffer = pool.acquire();try {// 使用Bufferbuffer.put("测试数据".getBytes());buffer.flip();// 处理数据...byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("处理的数据: " + new String(data));} finally {// 释放Buffer回池中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 {// 1. 📩📩📩📩📩📩📩 视图Buffer - 在不同数据类型间转换public static void demonstrateViewBuffers() {System.out.println("=== 视图Buffer示例 ===");// 创建ByteBufferByteBuffer byteBuffer = ByteBuffer.allocate(32);// 作为不同数据类型的视图byteBuffer.putInt(123);  // 写入intbyteBuffer.putDouble(45.67);  // 写入doublebyteBuffer.putChar('A');  // 写入char// 切换为读模式byteBuffer.flip();// 创建不同数据类型的视图IntBuffer intBuffer = byteBuffer.asIntBuffer();DoubleBuffer doubleBuffer = byteBuffer.asDoubleBuffer();CharBuffer charBuffer = byteBuffer.asCharBuffer();// 注意:视图Buffer共享底层数据,但有自己的position/limitSystem.out.println("Int视图: " + intBuffer.get());System.out.println("Double视图: " + doubleBuffer.get());System.out.println("Char视图: " + charBuffer.get());}// 2. 💯💯💯💯💯💯💯💯 字节序处理public static void demonstrateByteOrder() {System.out.println("\n=== 字节序示例 ===");ByteBuffer buffer = ByteBuffer.allocate(4);// 默认字节序(通常是BIG_ENDIAN)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();}// 3. 🎁🎁🎁🎁🎁🎁🎁 批量操作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();}// 4. ✅✅✅✅✅✅✅✅ 只读Bufferpublic 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();// 尝试修改只读Buffer会抛出ReadOnlyBufferExceptiontry {readOnly.put((byte) 'X');} catch (Exception e) {System.out.println("正确捕获异常: " + e.getClass().getSimpleName());}}// 5. 内存映射Bufferpublic 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 ⚠️ 常见错误

// ❌ 错误:忘记 flip()
buffer.put("data".getBytes());
// channel.write(buffer); // ❌ 写不出数据,因为 position=4, limit=10// ✅ 正确:必须 flip()
buffer.put("data".getBytes());
buffer.flip();
channel.write(buffer); // ✅ 正确

3.11 Buffer 的优缺点总结

3.11.1 ✅ 优点

优点说明
高性能块式读写,减少系统调用
内存控制明确的容量与边界
零拷贝支持DirectBufferMappedByteBuffer
线程安全单线程操作,无并发问题(通常)
类型丰富支持多种基本类型

3.11.2 ❌ 缺点

缺点说明
状态管理复杂position/limit/flip() 易出错
API 繁琐相比流式 IO 更复杂
堆外内存泄漏风险DirectBuffer 不受 GC 直接管理
学习成本高需理解读写模式切换

3.12 总结:Buffer 的核心价值

维度说明
核心角色NIO 的“数据容器”
设计思想块式 I/O 取代流式 I/O
核心能力统一的数据读写接口,支持高效 I/O
适用场景文件读写、网络通信、大文件处理
NIO 地位ChannelSelector 并列为三大基石

3.13 一句话总结

Buffer 是 Java NIO 的“心脏”

  • 它通过容量、位置、上限、标记四要素精确控制数据流,结合 flip() 等操作实现高效的读写切换
  • 为高性能 I/O 提供了坚实基础。掌握 Buffer,是掌握 NIO 的第一步。
http://www.xdnf.cn/news/1459693.html

相关文章:

  • 2025年百度商业AI技术创新大赛赛道二:视频广告生成推理性能优化-初赛第五名,复赛第九名方案分享
  • JVM 运行时数据区域
  • java面试中经常会问到的dubbo问题有哪些(基础版)
  • JVM 类加载全过程
  • Node-RED服务成本/价格很高?那这不到“三张”的怎么说?
  • QT卡顿的可能原因
  • TP8 数组在模板html文件中输出json字符串格式{“0“:“x1“,“1“:“x2“,“2“:“x3“}
  • 在Spring MVC中使用查询字符串与参数
  • 2025市面上比较实用的财会行业证书,最值得考的8个职业证书推荐
  • 本地部署开源数据生成器项目实战指南
  • HarmonyOS应用开发之界面列表不刷新问题Bug排查记:从现象到解决完整记录
  • JS函数进阶
  • Roo Code之自定义指令(Custom Instructions),规则(Rules)
  • 硬盘分区格式化后产生了哪些变化
  • OpenStack VLAN网络类型实训案例
  • 机器学习:后篇
  • LangChain4j的初步学习【逐步添加中】
  • 强化学习DQN解决Cart_Pole问题
  • claude code route 使用教程|命令大全
  • linux中的awk使用详解
  • 深度解读《实施“人工智能+”行动的意见》:一场由场景、数据与价值链共同定义的产业升级
  • 【8】C#上位机---泛型、委托delegate与多线程Task
  • 2025年代理IP服务深度评测:三大平台横评,谁是最强业务助手?
  • 检查数据集格式(77)
  • 计算机二级C语言操作题(填空、修改、设计题)——真题库(16)附解析答案
  • C++基础——模板进阶
  • 【C++题解】关联容器
  • Linux的权限详解
  • 一次死锁的排查
  • 激活函数:神经网络的“灵魂开关”