前言
这文章算是同事约稿(手动狗头),但是从搜集资料的过程中确实也学到了不少。
使用
申请堆外内存:使用unsafe.allocate,返回一个long类型,表示起始地址
使用:unsafe.putByte,unsafe.getByte
释放堆外内存:使用unsafe.free
JDK自动释放,使用虚引用注册一个DirectByteBuffer的即将被GC的Hook,在这个Hook中调用unsafe.free
使用场景
使用场景:
- JDK IO,对fd进行read和write时,申请一个堆外内存作为中转 -> [源码IOUtil]
- Netty作为ByteBuffer内存池
- OHC框架,Off-Heap-Cache,作为堆外缓存使用
优势和劣势
为什么使用堆外内存:
- IO友好,使用场景1
- 自己管理内存,减少堆内存占用,减轻JVM GC压力
劣势:
- 常见内存泄漏问题,难以排查
- 只能是byte[]对象,存储与获取其他对象,需要自己序列化和反序列化
- 性能问题(相对于GC消耗需要辩证看待)
- 申请和释放堆外内存消耗较大(NativeMethod,底层是malloc和free)
- 访问堆外内存速度不如访问JVM对象(”leave” the JVM “context”)
- 内存占用:如果使用内存池管理,实际占用比实际使用的较大
适用场景
适用于:
- IO的Buffer
- 单次使用:内存占用较大的,不影响JVM堆
- 频繁使用:
- 占用空间较大,对象存活时间较长,配合内存池使用:减轻GC压力,减少GC毛刺与抖动,范例OHC
- 生命周期较短,但是量多且涉及IO,配合内存池使用:范例Netty
补充阅读
- https://github.com/snazy/ohc
- 视频:Netty-A framework to rule them all,B站和Youtube均有,讲了Netty在内存使用上的优化
- Java未提供munmap方法:https://www.cnblogs.com/huxi2b/p/6637425.html
- 使用堆外内存优化GC,https://juejin.im/post/5cdf8df4f265da1bd260bae9
- 堆外内存溢出问题排查,https://coldwalker.com/2018/12//troubleshooter_directbytebuffer_memory_issue/