JAVA NIO 內存泄露

寫NIO程序經常使用ByteBuffer來讀取或者寫入數據,那麼使用ByteBuffer.allocate(capability)還是使用ByteBuffer.allocteDirect(capability)來分配緩存了?第一種方式是分配JVM堆內存,屬於GC管轄範圍,由於需要拷貝所以速度相對較慢;第二種方式是分配OS本地內存,不屬於GC管轄範圍,由於不需要內存拷貝所以速度相對較快。


我們肯定想選擇比較快的,但問題是直接內存不屬於GC管轄範圍,需要弄清楚這部分內存如何管理,否則造成內存泄露就麻煩了。本地內存在JAVA中有一個對應的包裝類DirectByteBuffer,該類屬於Java類,適當的時候會被GC回收,當它被回收前會調用本地方法把直接內存給釋放了,所以本地內存可以隨DirectByteBuffer對象被回收而自動回收,貌似沒有問題;但如果不斷分配本地內存,堆內存很少使用,那麼JVM就不需要執行GC,DirectByteBuffer對象們就不會被回收,這時候堆內存充足,但本地內存可能已經使用光了,再次嘗試分配本地內存就會出現OutOfMemoryError,那程序就直接崩潰了。


有沒有解決方案?自動釋放不靠譜,我們是否可以手動釋放本地內存,把握主動權?果然DirectByteBuffer持有一個Cleaner對象,該對象有一個clean()方法可用於釋放本地內存,所以需要的時候我們可以調用這個方法手動釋放本地內存。


以下代碼與測試場景幫助理解與證實以上描述。


代碼1:

package com.stevex.app.nio;

import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;

public class DirectByteBufferTest {
	public static void main(String[] args) throws InterruptedException{
	        //分配128MB直接內存
		ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*128);
		
		TimeUnit.SECONDS.sleep(10);
		System.out.println("ok");
	}

}


測試用例1:設置JVM參數-Xmx100m,運行異常,因爲如果沒設置-XX:MaxDirectMemorySize,則默認與-Xmx參數值相同,分配128M直接內存超出限制範圍。

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:658)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)
	at com.stevex.app.nio.DirectByteBufferTest.main(DirectByteBufferTest.java:8)


測試用例2:設置JVM參數-Xmx256m,運行正常,因爲128M小於256M,屬於範圍內分配。

ok


測試用例3:設置JVM參數-Xmx256m -XX:MaxDirectMemorySize=100M,運行異常,分配的直接內存128M超過限定的100M。

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
	at java.nio.Bits.reserveMemory(Bits.java:658)
	at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
	at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)
	at com.stevex.app.nio.DirectByteBufferTest.main(DirectByteBufferTest.java:8)


代碼2:

package com.stevex.app.nio;

import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import sun.nio.ch.DirectBuffer;

public class DirectByteBufferTest {
	public static void main(String[] args) throws InterruptedException{
		//分配512MB直接緩存
		ByteBuffer bb = ByteBuffer.allocateDirect(1024*1024*512);
		
		TimeUnit.SECONDS.sleep(10);
		
		//清除直接緩存
		((DirectBuffer)bb).cleaner().clean();
		
		TimeUnit.SECONDS.sleep(10);
		
		System.out.println("ok");
	}

}

測試用例4:設置JVM參數-Xmx768m,運行程序觀察內存使用變化,會發現clean()後內存馬上下降,說明使用clean()方法能有效及時回收直接緩存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章