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);//释放
  • 相关阅读:
    新建一个类并绑定一个activity
    关于fragment保存变量的问题
    关于使用别人方法的效率问题
    使用asynctask的问题
    关于整个头像更新问题(2)
    关于查找所需代码的问题
    静态变量的使用问题
    关于更换头像的整个过程理解
    fill_parent 和 match_parent区别
    关于ui修改的若干想法
  • 原文地址:https://www.cnblogs.com/holoyong/p/7266240.html
Copyright © 2011-2022 走看看