一、概述

ByteBuffer 属于 java.nio 包下的类,它是 Java 提供的堆外内存的 Buffer,主要用于 Java NIO 编程中。

二、源码

  1. DirectByteBuffer(int cap) {
  2. super(-1, 0, cap, cap, null);
  3. boolean pa = VM.isDirectMemoryPageAligned();
  4. int ps = Bits.pageSize();
  5. long size = Math.max(1L, (long)cap + (pa ? ps : 0));
  6. // 强制执行 Full GC,但一般生产环境都是设置了 -XX:DisableExplicitGC,所以是不起作用的
  7. Bits.reserveMemory(size, cap);
  8. long base = 0;
  9. try {
  10. base = UNSAFE.allocateMemory(size);
  11. } catch (OutOfMemoryError x) {
  12. Bits.unreserveMemory(size, cap);
  13. throw x;
  14. }
  15. UNSAFE.setMemory(base, size, (byte) 0);
  16. if (pa && (base % ps != 0)) {
  17. // Round up to page boundary
  18. address = base + ps - (base & (ps - 1));
  19. } else {
  20. address = base;
  21. }
  22. // 创建 Cleaner 对象,负责堆外内存回收工作。
  23. // 虚引用(PhantomReference)和 ReferenceQueue 配合使用达到回收堆外内存的目的
  24. // Deallocator 内部持有堆外内存基地址以及大小和容量,并有一个 run方法用以释放堆外内存
  25. // Cleaner 是一个双端链表,create包含add方法,添加到首部位置
  26. // 当 堆内的 DirectByteBuffer 对象被回收,同时也表示其指向的堆外内存也需要被回收,
  27. // 这时,Cleaner对象不再有任何引用关系,在下一次 GC 时,该 Cleaner对象将被添加到 ReferenceQueue
  28. // 中并执行 clean() 方法,它主要做两件事件
  29. // 1.将 Cleaner 对象从链表中移除
  30. // 2.调用 UNSAFE.freeMemory(address); 方法清理堆外内存
  31. cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
  32. att = null;
  33. }
  34. private static class Deallocator implements Runnable {
  35. private long address;
  36. private long size;
  37. private int capacity;
  38. private Deallocator(long address, long size, int capacity) {
  39. assert (address != 0);
  40. this.address = address;
  41. this.size = size;
  42. this.capacity = capacity;
  43. }
  44. public void run() {
  45. if (address == 0) {
  46. // Paranoia
  47. return;
  48. }
  49. // 释放堆外内存
  50. UNSAFE.freeMemory(address);
  51. address = 0;
  52. // 重置大小和容量
  53. Bits.unreserveMemory(size, capacity);
  54. }
  55. }