DirectByteBuffer实现原理分析

https://www.cnblogs.com/wangmh/p/7815791.html

1.创建DirectByteBuffer

  Direct ByteBuffer是通过JNI在Java虚拟机外的内存中分配了一块(所以即使在运行时通过-Xmx指定了Java虚拟机的最大堆内存,还是可能实例化超出该大小的Direct ByteBuffer),该内存块并不直接由Java虚拟机负责垃圾收集.

使用allocateDirect()静态方法创建对象分配内存

  ByteBuffer buffer=ByteBuffer.allocateDirect(256);

复制代码

 1 /**
 2  * Allocates a new direct byte buffer.
 3  *
 4  * <p> The new buffer's position will be zero, its limit will be its
 5  * capacity, its mark will be undefined, and each of its elements will be
 6  * initialized to zero.  Whether or not it has a
 7  * {@link #hasArray </code>backing array<code>} is unspecified.
 8  *
 9  * @param capacity The new buffer's capacity, in bytes
10  * @return The new byte buffer
11  * @throws IllegalArgumentException If the <tt>capacity</tt> is a negative integer
12  */
13 public static ByteBuffer allocateDirect(int capacity) {
14     if (capacity < 0) {
15         throw new IllegalArgumentException("capacity < 0: " + capacity);
16     }
17 
18     DirectByteBuffer.MemoryRef memoryRef = new DirectByteBuffer.MemoryRef(capacity);
19     return new DirectByteBuffer(capacity, memoryRef);
20 }
21 //
22 public MemoryRef(int capacity) {
23     VMRuntime runtime = VMRuntime.getRuntime();
24     buffer = (byte[]) runtime.newNonMovableArray(byte.class, capacity + 7);
25     allocatedAddress = runtime.addressOf(buffer);
26     // Offset is set to handle the alignment: http://b/16449607
27     offset = (int) (((allocatedAddress + 7) & ~(long) 7) - allocatedAddress);
28     isAccessible = true;
29     isFreed = false;
30 }

复制代码

 

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

DirectByteBuffer主要应用在android数据传递过程.减少数据与JNI数据拷贝转换操作

DirectByteBuffer.putInt(value);
下面分析一下当执行putInt后,DirectByteBuffer都执行了什么操作

复制代码

 1     //计算写数据的位置
 2     @Override
 3     public final ByteBuffer putInt(int x) {
 4         if (!memoryRef.isAccessible) {
 5             throw new IllegalStateException("buffer is inaccessible");
 6         }
 7         if (isReadOnly) {
 8             throw new ReadOnlyBufferException();
 9         }
10         putInt(ix(nextPutIndex(SizeOf.INT)), x);
11         return this;
12     }
13     //调用Memory来完成Int数据存储
14     private ByteBuffer putInt(long a, int x) {
15         Memory.pokeInt(a, x, !nativeByteOrder);
16         return this;
17     }
18 
19     private long ix(int i) {
20         return address + i;
21     }
22     

复制代码

再往下看Memory做了什么

复制代码

1 public static void pokeInt(long address, int value, boolean swap) { 
2        if (swap) {
3             value = Integer.reverseBytes(value);
4         }
5         pokeIntNative(address, value);
6     }
7 //因为最后执行到JNI层.这块就不涉及到字节序的问题
8 private static native void pokeIntNative(long address, int value);

复制代码

 

当执行 DirectByteBuffer.getInt();都执行了哪些操作

复制代码

 1 public int getInt() {
 2     if (!memoryRef.isAccessible) {
 3        throw new IllegalStateException("buffer is inaccessible");
 4     }
 5     return getInt(ix(nextGetIndex(SizeOf.INT)));
 6 }
 7 
 8 //最后执行Memory 的JNI方法
 9 private int getInt(long a) {
10         return Memory.peekInt(a, !nativeByteOrder);
11 }
12 //Memory 执行的操作
13 public static int peekInt(long address, boolean swap) {
14     int result = peekIntNative(address);
15     if (swap) {
16         result = Integer.reverseBytes(result);
17     }
18     return result;
19 }
20 private static native int peekIntNative(long address);

复制代码

所以得出结论就本身在Java中引入DirectByteBuffer并不会提高性能

<think>好的,我现在要帮用户理解Netty框架中的零拷贝技术,包括实现原理和使用示例。首先,我需要回忆一下Netty的零拷贝相关知识,并结合用户提供的三个引用内容来构建回答。 用户给出的引用中,提到Netty的零拷贝与传统Linux的不同,更多是用户态的数据操作优化,包括五个方面。引用2和3提到了堆内存分配和内核与用户态之间的数据传输,以及零拷贝如何减少上下文切换。需要将这些点整合到回答中。 首先,零拷贝在操作系统层面通常指减少数据拷贝次数,比如通过sendfile等技术。但Netty的零拷贝更侧重于在用户态优化数据操作,减少不必要的复制。例如,CompositeByteBuffer可以组合多个Buffer,避免合并时的复制。还有直接使用堆外内存、文件传输优化等。 接下来要分点说明实现原理,可能需要包括CompositeByteBuffer、堆外内存、文件区域封装、内存池和切片操作。每个点都需要结合引用中的内容,比如引用2提到的HeapByteBuffer,引用3提到的kernel和user模式的切换减少。 然后,使用示例部分需要展示代码,比如如何组合Buffer,使用FileRegion传输文件,以及使用Unpooled.wrappedBuffer包装数据。这些示例要具体,说明如何避免内存复制。 还要生成相关问题,可能涉及与传统零拷贝的区别、CompositeByteBuffer的作用、内存池的关系、适用场景和性能对比等。 需要注意的是,用户之前的问题可能已经涉及了零拷贝的基本概念,所以这次回答要更深入原理和示例,同时引用提供的资料来支撑内容。确保LaTeX格式正确,行内公式用$,独立公式用$$,但可能在这个回答中用不到太多数学公式,除非涉及性能分析的数据。 检查是否有遗漏点,比如Netty的其他优化技术,比如切片保留共享数据,避免复制。同时要确保回答结构清晰,分点明确,代码示例正确,并且引用标记正确,比如引用1、2、3对应到相应的解释部分。 最后,生成的相关问题需要引导用户进一步探索,可能包括实现细节、优缺点、实际应用中的问题等。确保问题与回答内容紧密相关,帮助用户深入理解。</think>### Netty零拷贝实现原理及使用示例 #### 一、实现原理 Netty的零拷贝技术主要面向**用户态数据操作优化**,通过减少数据复制次数和上下文切换提升性能,具体体现在以下方面: 1. **CompositeByteBuffer组合缓冲区** 通过$CompositeByteBuffer$将多个$ByteBuffer$逻辑组合为连续视图,避免物理合并时的内存复制[^1]。 例如:将协议头和消息体合并时,无需实际拼接。 2. **堆外内存直接操作** 使用$DirectByteBuffer$分配堆外内存,允许JVM直接访问系统内存,减少内核态与用户态的数据拷贝[^2]。 ```java ByteBuffer directBuf = ByteBuffer.allocateDirect(1024); ``` 3. **文件传输优化** 封装$FileRegion$对象,利用操作系统的$sendfile()$系统调用实现文件到网络的直接传输[^3]。 $$数据传输量 = 文件大小 \quad (无需中间缓冲区)$$ 4. **内存池复用机制** 预先分配内存块并重复利用,减少频繁申请/释放内存的开销。 5. **切片共享数据** 通过$slice()$或$duplicate()$创建缓冲区视图,共享底层数据而非复制。 --- #### 二、使用示例 1. **组合多个缓冲区** ```java ByteBuffer header = Unpooled.buffer(8); ByteBuffer body = Unpooled.buffer(128); CompositeByteBuffer compositeBuf = Unpooled.compositeBuffer(); compositeBuf.addComponents(true, header, body); // 逻辑组合 ``` 2. **文件零拷贝传输** ```java FileChannel fileChannel = new FileInputStream("data.txt").getChannel(); FileRegion region = new DefaultFileRegion(fileChannel, 0, fileChannel.size()); channel.write(region); // 直接触发sendfile调用 ``` 3. **包装现有数据** ```java byte[] rawData = new byte[1024]; ByteBuf wrappedBuf = Unpooled.wrappedBuffer(rawData); // 无拷贝包装 ``` --- #### 三、性能对比 | 操作类型 | 传统方式拷贝次数 | Netty零拷贝次数 | |-------------------|------------------|-----------------| | 合并两个缓冲区 | 2次 | 0次 | | 发送文件 | 4次[^3] | 1次 | | 切片操作 | 1次 | 0次 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值