0%

Java堆外内存理解

前言

这文章算是同事约稿(手动狗头),但是从搜集资料的过程中确实也学到了不少。

使用

申请堆外内存:使用unsafe.allocate,返回一个long类型,表示起始地址

使用:unsafe.putByte,unsafe.getByte

释放堆外内存:使用unsafe.free

JDK自动释放,使用虚引用注册一个DirectByteBuffer的即将被GC的Hook,在这个Hook中调用unsafe.free

使用场景

使用场景:

  1. JDK IO,对fd进行read和write时,申请一个堆外内存作为中转 -> [源码IOUtil]
    1. 为啥?
    2. 解答:R大的解答:https://www.zhihu.com/question/57374068/answer/152691891
  2. Netty作为ByteBuffer内存池
  3. OHC框架,Off-Heap-Cache,作为堆外缓存使用

优势和劣势

为什么使用堆外内存:

  1. IO友好,使用场景1
  2. 自己管理内存,减少堆内存占用,减轻JVM GC压力

劣势:

  1. 常见内存泄漏问题,难以排查
  2. 只能是byte[]对象,存储与获取其他对象,需要自己序列化和反序列化
  3. 性能问题(相对于GC消耗需要辩证看待)
    1. 申请和释放堆外内存消耗较大(NativeMethod,底层是malloc和free)
    2. 访问堆外内存速度不如访问JVM对象(”leave” the JVM “context”)
  4. 内存占用:如果使用内存池管理,实际占用比实际使用的较大

适用场景

适用于:

  1. IO的Buffer
  2. 单次使用:内存占用较大的,不影响JVM堆
  3. 频繁使用:
    1. 占用空间较大,对象存活时间较长,配合内存池使用:减轻GC压力,减少GC毛刺与抖动,范例OHC
    2. 生命周期较短,但是量多且涉及IO,配合内存池使用:范例Netty

补充阅读

  1. https://github.com/snazy/ohc
  2. 视频:Netty-A framework to rule them all,B站和Youtube均有,讲了Netty在内存使用上的优化
  3. Java未提供munmap方法:https://www.cnblogs.com/huxi2b/p/6637425.html
  4. 使用堆外内存优化GC,https://juejin.im/post/5cdf8df4f265da1bd260bae9
  5. 堆外内存溢出问题排查,https://coldwalker.com/2018/12//troubleshooter_directbytebuffer_memory_issue/