【Java】 DirectByteBuffer堆外內存回收

PhantomReference虛引用在分析堆外內存回收之前,先了解下PhantomReference虛引用 。
PhantomReference需要與ReferenceQueue引用隊列結合使用,在GC進行垃圾回收的時候 , 如果發現一個對象只有虛引用在引用它,則認為該對象需要被回收,會將引用該對象的虛引用加入到與其關聯的ReferenceQueue隊列中 , 開發者可以通過ReferenceQueue獲取需要被回收的對象,然后做一些清理操作,從隊列中獲取過的元素會從隊列中清除 , 之后GC就可以對該對象進行回收 。
虛引用提供了一種追蹤對象垃圾回收狀態的機制,讓開發者知道哪些對象準備進行回收,在回收之前開發者可以進行一些清理工作,之后GC就可以將對象進行真正的回收 。
來看一個虛引用的使用例子:

  1. 創建一個ReferenceQueue隊列queue,用于跟蹤對象的回收;
  2. 創建一個obj對象,通過new創建的是強引用,只要強引用存在,對象就不會被回收;
  3. 創建一個虛引用PhantomReference,將obj對象和ReferenceQueue隊列傳入,此時phantomReference里面引用了obj對象,并關聯著引用隊列queue;
  4. 同樣的方式創建另一個obj1對象和虛引用對象phantomReference1;
  5. 將obj置為NULL,此時強引用關系失效;
  6. 調用 System.gc()進行垃圾回收;
  7. 由于obj的強引用關系失效 , 所以GC認為該對象需要被回收 , 會將引用該對象的虛引用phantomReference對象放入到與其關聯的引用隊列queue中;
  8. 通過poll從引用隊列queue中獲取對象,可以發現會獲取到phantomReference對象,poll獲取之后會將對象從引用隊列中刪除 , 之后會被垃圾回收器回收;
  9. obj1的強引用關系還在,所以從queue中并不會獲取到;
public static void main(String[] args) {// 創建引用隊列ReferenceQueue<Object> queue = new ReferenceQueue<Object>();// 創建obj對象Object obj = new Object();// 創建虛引用,虛引用引用了obj對象,并與queue進行關聯PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj, queue);// 創建obj1對象Object obj1 = new Object();PhantomReference<Object> phantomReference1 = new PhantomReference<Object>(obj1, queue);// 將obj置為NULL,強引用關系失效obj = null;// 垃圾回收System.gc();// 從引用隊列獲取對象Object o = queue.poll();if (null != o) {System.out.println(o.toString());}}輸出結果:
java.lang.ref.PhantomReference@277c0f21
【Java】 DirectByteBuffer堆外內存回收

文章插圖
Reference實例的幾種狀態
Active:初始狀態,創建一個Reference類型的實例之后處于Active狀態,以上面虛引用為例,通過new創建一個PhantomReference虛引用對象之后,虛引用對象就處于Active狀態 。
Pending:當GC檢測到對象的可達性發生變化時,會根據是否關聯了引用隊列來決定是否將狀態更改為Pending或者Inactive,虛引用必須與引用隊列結合使用,所以對于虛引用來說,如果它實際引用的對象需要被回收,垃圾回收器會將這個虛引用對象加入到一個Pending列表中,此時處于Pending狀態 。
同樣以上面的的虛引用為例,因為obj的強引用關系失效,GC就會把引用它的虛引用對象放入到pending列表中 。
Enqueued:表示引用對象被加入到了引用隊列,Reference有一個后臺線程去檢測是否有處于Pending狀態的引用對象 , 如果有會將引用對象加入到與其關聯的引用隊列中,此時由Pending轉為Enqueued狀態表示對象已加入到引用隊列中 。
Inactive:通過引用隊列的poll方法可以從引用隊列中獲取引用對象,同時引用對象會從隊列中移除,此時引用對象處于Inactive狀態,之后會被GC回收 。
DirectByteBuffer堆外內存回收在DirectByteBuffer的構造函數中,在申請內存之前,先調用了BitsreserveMemory方法回收內存,申請內存之后,調用Cleanercreate方法創建了一個Cleaner對象 , 并傳入了當前對象(DirectByteBuffer)和一個Deallocator類型的對象:
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {private final Cleaner cleaner;DirectByteBuffer(int cap) {// package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));// 清理內存Bits.reserveMemory(size, cap);long base = 0;try {// 分配內存base = unsafe.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}// 創建Cleader,傳入了當前對象和Deallocatorcleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}}

推薦閱讀