標籤:dir error pos attach cti 實現 runnable nal 等級
我們可以通過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 boundary20 address = base + ps - (base & (ps - 1));21 } else {22 //address是始址,可知一個DirectByteBufferObject Storage Service了記憶體始址address、記憶體容量capacity,這已經可以確定一塊記憶體了,再加上position、limited、mark就可以對該記憶體進行緩衝式的讀寫操作了23 address = base;24 }25 cleaner = Cleaner.create(this, new Deallocator(base, size, cap));//用於回收直接記憶體26 att = null;//attachement27 }
對於記憶體空間,我們關注的是它的分配和回收,這裡使用了unsafe分配,unsafe是一個提供了低等級操作的介面,這裡就不研究它了,我們主要來看這塊被unsafe分配的直接記憶體是如何被回收的。
重點來看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 // Paranoia21 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是Deallocator3 return var1 == null ? null : add(new Cleaner(var0, var1));4 }5 private Cleaner(Object var1, Runnable var2) {6 //Cleaner繼承了PhantomReference,dummyQueue是一個假隊列,無用。這裡將DirectByteBuffer作為PhantomReference,Deallocator為thunk7 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);//釋放
java基礎-DirectByteBuffer