zoukankan      html  css  js  c++  java
  • java基础-DirectByteBuffer

    我们可以通过ByteBuffer创建一块直接内存

    ByteBuffer direct = ByteBuffer.allocateDirect(8);//创建8字节空间的直接内存

     来看这块内存是如何被分配的

    1     public static ByteBuffer allocateDirect(int capacity) {
    2         return new DirectByteBuffer(capacity);
    3     }
     1     DirectByteBuffer(int cap) {                   // package-private
     2         //mark, position, limit, capacity
     3         super(-1, 0, cap, cap);
     4         //后面的一大堆就是计算分配空间大小、分配、计算空间始址
     5         boolean pa = VM.isDirectMemoryPageAligned();
     6         int ps = Bits.pageSize();
     7         long size = Math.max(1L, (long)cap + (pa ? ps : 0));
     8         Bits.reserveMemory(size, cap);
     9 
    10         long base = 0;
    11         try {
    12             base = unsafe.allocateMemory(size);//使用unsafe来分配直接内存
    13         } catch (OutOfMemoryError x) {
    14             Bits.unreserveMemory(size, cap);
    15             throw x;
    16         }
    17         unsafe.setMemory(base, size, (byte) 0);
    18         if (pa && (base % ps != 0)) {
    19             // Round up to page boundary
    20             address = base + ps - (base & (ps - 1));
    21         } else {
    22             //address是始址,可知一个DirectByteBuffer对象存储了内存始址address、内存容量capacity,这已经可以确定一块内存了,再加上position、limited、mark就可以对该内存进行缓存式的读写操作了
    23             address = base;
    24         }
    25         cleaner = Cleaner.create(this, new Deallocator(base, size, cap));//用于回收直接内存
    26         att = null;//attachement
    27     }

    对于内存空间,我们关注的是它的分配和回收,这里使用了unsafe分配,unsafe是一个提供了低等级操作的接口,这里就不研究它了,我们主要来看这块被unsafe分配的直接内存是如何被回收的

    我们知道,ByteBuffer和普通java对象一样,是通过gc回收的,但gc并不管理直接内存,ByteBuffer指向的直接内存空间是如何被释放的呢?

    重点来看Cleaner.create(this, new Deallocator(base, size, cap))

    Deallocator实现了Runnable,在run方法中使用unsafe.freeMemory(address)释放了内存。

     1     private static class Deallocator
     2         implements Runnable
     3     {
     4 
     5         private static Unsafe unsafe = Unsafe.getUnsafe();
     6 
     7         private long address;
     8         private long size;
     9         private int capacity;
    10 
    11         private Deallocator(long address, long size, int capacity) {
    12             assert (address != 0);
    13             this.address = address;
    14             this.size = size;
    15             this.capacity = capacity;
    16         }
    17 
    18         public void run() {
    19             if (address == 0) {
    20                 // Paranoia
    21                 return;
    22             }
    23             unsafe.freeMemory(address);
    24             address = 0;
    25             Bits.unreserveMemory(size, capacity);
    26         }
    27 
    28     }
    1     public static Cleaner create(Object var0, Runnable var1) {
    2         //add是将cleaner实例存入由Cleaner维护的一个链表中,这里的var0是DirectByteBuffer,var1是Deallocator
    3         return var1 == null ? null : add(new Cleaner(var0, var1));
    4     }
    5     private Cleaner(Object var1, Runnable var2) {
    6         //Cleaner继承了PhantomReference,dummyQueue是一个假队列,无用。这里将DirectByteBuffer作为PhantomReference,Deallocator为thunk
    7         super(var1, dummyQueue);
    8         this.thunk = var2;
    9     }

    之前有聊过Reference机制对于Cleaner的特殊处理,当HandlerThread从pending队列中取到cleaner后,会执行其clean方法。下面就是clean方法,其中调用了thunk.run,该thunk对应Deallocator,run方法中就包含了unsafe.freeMemory,就此直接内存被释放了。

     1     public void clean() {
     2         if (remove(this)) {//从链表中删除该cleaner
     3             try {
     4                 this.thunk.run();//执行Runnable逻辑
     5             } catch (final Throwable var2) {
     6                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
     7                     public Void run() {
     8                         if (System.err != null) {
     9                             (new Error("Cleaner terminated abnormally", var2)).printStackTrace();
    10                         }
    11 
    12                         System.exit(1);
    13                         return null;
    14                     }
    15                 });
    16             }
    17         }
    18     }

    总结一下,申请直接内存其实就是构造了一个DirectByteBuffer实例,该实例会持有一个cleaner实例,当不再有强引用指向我们创建的DirectByteBuffer实例时,gc就会回收该实例,与此同时,PhantomReference类型的Cleaner也会被HandlerThread捕获,并执行clean方法,该clean方法会调用thunk.run,对于DirectByteBuffer来说,thunk就是Deallocator,故直接内存得以释放。

    由上述可知,我们也可以自己控制直接内存的分配和释放

    1         long address = unsafe.allocateMemory(size);//分配
    2         unsafe.freeMemory(address);//释放
  • 相关阅读:
    STM32 F4 DAC DMA Waveform Generator
    STM32 F4 General-purpose Timers for Periodic Interrupts
    Python第十四天 序列化 pickle模块 cPickle模块 JSON模块 API的两种格式
    Python第十三天 django 1.6 导入模板 定义数据模型 访问数据库 GET和POST方法 SimpleCMDB项目 urllib模块 urllib2模块 httplib模块 django和web服务器整合 wsgi模块 gunicorn模块
    查看SQL Server服务运行帐户和SQL Server的所有注册表项
    Pycharm使用技巧(转载)
    SQL Server 2014内存优化表的使用场景
    Python第十天 print >> f,和fd.write()的区别 stdout的buffer 标准输入 标准输出 从控制台重定向到文件 标准错误 重定向 输出流和输入流 捕获sys.exit()调用 optparse argparse
    Python第七天 函数 函数参数 函数里的变量 函数返回值 多类型传值 函数递归调用 匿名函数 内置函数
    Python第六天 类型转换
  • 原文地址:https://www.cnblogs.com/holoyong/p/7266240.html
Copyright © 2011-2022 走看看