ByteBuffer 的堆内、堆外到底有什么区别?堆外内存什么时候释放、上限在哪?这篇用 BufferPoolMXBean 和几段小程序量出来(flip/clear/compact 的位置语义已有专门文章)。
堆内:isDirect = false,hasArray = true;堆外:isDirect = true,hasArray = false(堆外没有可访问的 byte[]) 默认字节序 = BIG_ENDIAN;putInt(1) 后第一个字节 = 0、最后一个字节 = 1;改成 LITTLE_ENDIAN 后重写:[1, 0, 0, 0] slice() 与原 buffer 共享内容:原 buffer 的第 0 个字节被 slice 改成了 99(写 slice 就是写原来的数据);position 与 limit 各自独立:原 0/8,slice 0/8 asReadOnlyBuffer() 之后写 → ReadOnlyBufferException(但它仍然看得到原 buffer 的修改:ro.get(0) = 99)
分配 1 MB 的 buffer(第 3 轮,前两轮预热):堆内 21 µs/个(2000 个),堆外 100 µs/个(200 个) FileChannel 写 512 MB(每次 1 MB 堆内 buffer):103 ms FileChannel 写 512 MB(每次 1 MB 堆外 buffer):157 ms FileChannel 写 512 MB(每次 1 MB 堆内 buffer):175 ms FileChannel 写 512 MB(每次 1 MB 堆外 buffer):148 ms
起始:堆外已用 0 KB,个数 0 分配 10 个 1 MB 堆外 buffer 后:堆外已用 10240 KB,个数 10(堆内存占用几乎不变:堆已用 3 MB) 丢掉引用(keep.clear())之后:堆外已用 10240 KB(没有立刻释放) System.gc() 之后:堆外已用 0 KB,个数 0(靠 GC 触发清理) 没有设上限时分配 100 MB 堆外:成功
已成功分配 64 MB 后,再分配 1 MB → OutOfMemoryError: Cannot reserve 1048576 bytes of direct buffer memory (allocated: 67108864, limit: 67108864) 此时堆内存几乎没用:堆已用 1 MB(-Xmx 是 256 MB)