要想获得一个 Buffer 对象首先要进行分配。
每一个 Buffer 类都有 allocate 方法(可以在堆上分配,也可以在直接内存上分配)。

  1. System.out.println(ByteBuffer.allocate(16).getClass());
  2. 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,一个数组不够,把多出来的内容写入新的数组,与前面的区别是消息存储不连续解析复杂,优点是避免了拷贝引起的性能损耗
  1. public class AllocateBuffer {
  2. public static void main(String[] args) {
  3. OperatingSystemMXBean osmxb = (OperatingSystemMXBean)
  4. ManagementFactory.getOperatingSystemMXBean();
  5. System.out.println("----------Test allocate--------");
  6. System.out.println("before allocate:"
  7. + osmxb.getFreePhysicalMemorySize());
  8. /*堆上分配*/
  9. ByteBuffer buffer = ByteBuffer.allocate(200000);
  10. System.out.println("buffer = " + buffer);
  11. System.out.println("after allocate:"
  12. + osmxb.getFreePhysicalMemorySize());
  13. /* 这部分用的直接内存*/
  14. ByteBuffer directBuffer = ByteBuffer.allocateDirect(200000);
  15. System.out.println("directBuffer = " + directBuffer);
  16. System.out.println("after direct allocate:"
  17. + osmxb.getFreePhysicalMemorySize());
  18. System.out.println("----------Test wrap--------");
  19. byte[] bytes = new byte[32];
  20. buffer = ByteBuffer.wrap(bytes);
  21. System.out.println(buffer);
  22. buffer = ByteBuffer.wrap(bytes, 10, 10);
  23. System.out.println(buffer);
  24. }
  25. }