在nio之前,是沒有光明正大的作法的,有一個work around的辦法是直接訪問Unsafe類。若是你使用Eclipse,默認是不容許訪問sun.misc下面的類的,你須要稍微修改一下,給Type Access Rules裏面添加一條全部類均可以訪問的規則:java
在使用Unsafe類的時候:數據庫
Unsafe f = Unsafe.getUnsafe();
發現仍是被拒絕了,拋出異常:數組
java.lang.SecurityException: Unsafe
正如Unsafe的類註釋中寫道:ide
Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.this
因而,只能使用反射來作這件事; 設計
Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe us = (Unsafe) f.get(null); long id = us.allocateMemory(1024 * 1024 * 1024);
其中,allocateMemory返回一個指針,而且其中的數據是未初始化的。若是要釋放這部份內存的話,須要調用freeMemory或者reallocateMemory方法。Unsafe對象提供了一系列put/get方法,例如putByte,可是隻能一個一個byte地put,我不知道這樣會不會影響效率,爲何不提供一個putByteArray的方法呢?指針
示例:code
import sun.misc.Unsafe; public class ObjectInHeap { private long address = 0; private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance(); public ObjectInHeap() { address = unsafe.allocateMemory(2 * 1024 * 1024); } // Exception in thread "main" java.lang.OutOfMemoryError public static void main(String[] args) { while (true) { ObjectInHeap heap = new ObjectInHeap(); System.out.println("memory address=" + heap.address); } } }
這段代碼會拋出OutOfMemoryError。這是由於ObjectInHeap對象是在堆內存中分配的,當該對象被垃圾回收的時候,並不會釋放堆外內存,由於使用Unsafe獲取的堆外內存,必須由程序顯示的釋放,JVM不會幫助咱們作這件事情。因而可知,使用Unsafe是有風險的,很容易致使內存泄露。對象
四、正確釋放Unsafe分配的堆外內存blog
雖然第3種狀況的ObjectInHeap存在內存泄露,可是這個類的設計是合理的,它很好的封裝了直接內存,這個類的調用者感覺不到直接內存的存在。那怎麼解決ObjectInHeap中的內存泄露問題呢?能夠覆寫Object.finalize(),當堆中的對象即將被垃圾回收器釋放的時候,會調用該對象的finalize。因爲JVM只會幫助咱們管理內存資源,不會幫助咱們管理數據庫鏈接,文件句柄等資源,因此咱們須要在finalize本身釋放資源。
import sun.misc.Unsafe; public class RevisedObjectInHeap { private long address = 0; private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance(); // 讓對象佔用堆內存,觸發[Full GC private byte[] bytes = null; public RevisedObjectInHeap() { address = unsafe.allocateMemory(2 * 1024 * 1024); bytes = new byte[1024 * 1024]; } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize." + bytes.length); unsafe.freeMemory(address); } public static void main(String[] args) { while (true) { RevisedObjectInHeap heap = new RevisedObjectInHeap(); System.out.println("memory address=" + heap.address); } } }
咱們覆蓋了finalize方法,手動釋放分配的堆外內存。若是堆中的對象被回收,那麼相應的也會釋放佔用的堆外內存。這裏有一點須要注意下:
// 讓對象佔用堆內存,觸發[Full GC private byte[] bytes = null;
這行代碼主要目的是爲了觸發堆內存的垃圾回收行爲,順帶執行對象的finalize釋放堆外內存。若是沒有這行代碼或者是分配的字節數組比較小,程序運行一段時間後仍是會報OutOfMemoryError。這是由於每當建立1個RevisedObjectInHeap對象的時候,佔用的堆內存很小(就幾十個字節左右),可是卻須要佔用2M的堆外內存。這樣堆內存還很充足(這種狀況下不會執行堆內存的垃圾回收),可是堆外內存已經不足,因此就不會報OutOfMemoryError。