要想获得一个 Buffer 对象首先要进行分配。
每一个 Buffer 类都有 allocate 方法(可以在堆上分配,也可以在直接内存上分配)。
System.out.println(ByteBuffer.allocate(16).getClass());
System.out.println(ByteBuffer.allocateDirect(16).getClass());
一、allocate() 堆上分配
分配 48 字节 capacity 的 ByteBuffer: ByteBuffer buf = ByteBuffer.allocate(48); :::tips
class java.nio.HeapByteBuffer - java 堆内存,读写效率较低,会受到 GC(垃圾回收) 的影响 :::
分配一个可存储 1024 个字符的 CharBuffer:CharBuffer buf = CharBuffer.allocate(1024);
wrap()
把一个 byte 数组或 byte 数组的一部分包装成 ByteBuffer:
ByteBuffer wrap(byte [] array)
- ByteBuffer wrap(byte [] array, int offset, int length)
二、allocateDirect()
:::tips
- class java.nio.DirectByteBuffer - 直接内存,读写效率高(少一次拷贝),不会受 GC 影响
但是因为属于系统内存,需要调用操作系统的函数,分配的效率低 :::
HeapByteBuffer与 DirectByteBuffer
HeapByteBuffer可以看出分配的 buffer 是在 heap 区域的,其实真正 flush 到远程的时候会先拷贝到直接内存,再做下一步操作;
- 在 NIO 的框架下,很多框架会采用 DirectByteBuffer 来操作,这样分配的内存不再是在 java heap 上,经过性能测试,可以得到非常快速的网络交互,在大量的网络交互下,一般速度会比 HeapByteBuffer 要快速好几倍。
ByteBuffer 大小分配
- 每个 channel 都需要记录可能被切分的消息,因为 ByteBuffer 不能被多个 channel 共同使用,因此需要为每个 channel 维护一个独立的 ByteBuffer
- ByteBuffer 不能太大,比如一个 ByteBuffer 1Mb 的话,要支持百万连接就要 1Tb 内存,因此需要设计大小可变的 ByteBuffer
- 一种思路是首先分配一个较小的 buffer,例如 4k,如果发现数据不够,再分配 8k 的 buffer,将 4k buffer 内容拷贝至 8k buffer,优点是消息连续容易处理,缺点是数据拷贝耗费性能,参考实现 http://tutorials.jenkov.com/java-performance/resizable-array.html
- 另一种思路是用多个数组组成 buffer,一个数组不够,把多出来的内容写入新的数组,与前面的区别是消息存储不连续解析复杂,优点是避免了拷贝引起的性能损耗
- 一种思路是首先分配一个较小的 buffer,例如 4k,如果发现数据不够,再分配 8k 的 buffer,将 4k buffer 内容拷贝至 8k buffer,优点是消息连续容易处理,缺点是数据拷贝耗费性能,参考实现 http://tutorials.jenkov.com/java-performance/resizable-array.html
public class AllocateBuffer {
public static void main(String[] args) {
OperatingSystemMXBean osmxb = (OperatingSystemMXBean)
ManagementFactory.getOperatingSystemMXBean();
System.out.println("----------Test allocate--------");
System.out.println("before allocate:"
+ osmxb.getFreePhysicalMemorySize());
/*堆上分配*/
ByteBuffer buffer = ByteBuffer.allocate(200000);
System.out.println("buffer = " + buffer);
System.out.println("after allocate:"
+ osmxb.getFreePhysicalMemorySize());
/* 这部分用的直接内存*/
ByteBuffer directBuffer = ByteBuffer.allocateDirect(200000);
System.out.println("directBuffer = " + directBuffer);
System.out.println("after direct allocate:"
+ osmxb.getFreePhysicalMemorySize());
System.out.println("----------Test wrap--------");
byte[] bytes = new byte[32];
buffer = ByteBuffer.wrap(bytes);
System.out.println(buffer);
buffer = ByteBuffer.wrap(bytes, 10, 10);
System.out.println(buffer);
}
}