Java直接内存-阿里云开发者社区

开发者社区> 开发与运维> 正文

Java直接内存

简介: Java直接内存, ByteBuffer,DirectByteBuffer,PhantomReference,ReferenceHandler
  • 直接内存申请空间其实是比较消耗性能,需要本地方法通过系统调用完成
  • 直接内存在IO读写上的性能要优于堆内存,所以直接内存特别适合申请以后进行多次读写
  • 堆外内存优势在 IO 操作上,对于网络 IO,使用 Socket 发送数据时,能够节省堆内存到堆外内存的数据拷贝
  • 零拷贝

    • 复制很大的文件
    • 频繁的IO操作,例如网络并发场景
  • 不受JVM堆的大小限制
  • 会受到本机总内存(包括RAM及SWAP区或者分页文件)的大小及处理器寻址空间的限制,可能会抛出OutOfMemoryError异常
  • 直接内存的最大大小可以通过-XX:MaxDirectMemorySize来设置,默认是64M
  • ByteBuffer.allocateDirect()
  • 由于申请内存前可能会调用 System.gc(),所以谨慎设置 -XX:+DisableExplicitGC 这个选项,这个参数作用是禁止代码中显示触发的 Full GC
  • 回收

    • 自动回收
    • GC 时会扫描 DirectByteBuffer 对象是否有有效引用指向该对象,如没有,在回收 DirectByteBuffer 对象的同时且会回收其占用的堆外内存
    • 虚引用(Phantom Reference)

      • 也称为幽灵引用或者幻影引用
      • 一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。
      • 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知
      • GC过程中如果发现某个对象除了只有PhantomReference引用它之外,并没有其他的地方引用它了,那将会把这个引用放到java.lang.ref.Reference.pending队列里,ReferenceHandler这个守护线程会处理pending队列里,执行一些后置处理,这里是调用Cleaner的clean方法
      • 而DirectByteBuffer构造方法内创建了一个Cleaner对象, Cleaner继承了PhantomReference,其referent为DirectByteBuffer,也是通过Cleaner调用unsafe.freeMemory(address)来释放直接内存
    • 手动回收

      • DirectByteBuffer 实现了 DirectBuffer 接口,这个接口有 cleaner 方法可以获取 cleaner 对象。
      public static void clean(final ByteBuffer byteBuffer) {  
        if (byteBuffer.isDirect()) {  
            ((DirectBuffer)byteBuffer).cleaner().clean();  
        }  
      }
      • Netty 中的堆外内存池就是使用反射来实现手动回收方式进行回收的。
      • 当堆空间非常富余,直接内存占用虽然很高,但并不会自发引起 GC——哪怕直接内存已经用满。 如果不触发 GC,直接内存可能就会溢出。因此,只能人为去触发 GC,从而回收直接内存。
  • DirectByteBuffer 这个类是 JDK 提供使用堆外内存的一种途径,当然常见的业务开发一般不会接触到,即使涉及到也可能是框架(如 Netty、RPC 等)使用的,对框架使用者来说也是透明的

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章