java directbuffer_java基础-DirectByteBuffer

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

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

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

1 public static ByteBuffer allocateDirect(intcapacity) {2 return newDirectByteBuffer(capacity);3 }

1 DirectByteBuffer(int cap) { //package-private2 //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 throwx;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 classDeallocator2 implementsRunnable3 {4

5 private static Unsafe unsafe =Unsafe.getUnsafe();6

7 private longaddress;8 private longsize;9 private intcapacity;10

11 private Deallocator(long address, long size, intcapacity) {12 assert (address != 0);13 this.address =address;14 this.size =size;15 this.capacity =capacity;16 }17

18 public voidrun() {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 staticCleaner create(Object var0, Runnable var1) {2 //add是将cleaner实例存入由Cleaner维护的一个链表中,这里的var0是DirectByteBuffer,var1是Deallocator

3 return var1 == null ? null : add(newCleaner(var0, var1));4 }5 privateCleaner(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 voidclean() {2 if (remove(this)) {//从链表中删除该cleaner

3 try{4 this.thunk.run();//执行Runnable逻辑

5 } catch (finalThrowable var2) {6 AccessController.doPrivileged(new PrivilegedAction() {7 publicVoid 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);//释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值