Java堆外內存管理

Java堆外內存管理java

 

一、JVM可使用的內存分外2種:堆內存和堆外內存函數

堆內存徹底由JVM負責分配和釋放,若是程序沒有缺陷代碼致使內存泄露,那麼就不會遇到java.lang.OutOfMemoryError這個錯誤。性能

使用堆外內存,就是爲了能直接分配和釋放內存,提升效率。JDK5.0以後,代碼中能直接操做本地內存的方式有2種:使用未公開的Unsafe和NIO包下ByteBuffer。測試

 使用ByteBuffer分配本地內存則很是簡單,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)便可。spa

C語言的內存分配和釋放函數malloc/free,必需要一一對應,不然就會出現內存泄露或者是野指針的非法訪問。java中咱們須要手動釋放獲取的堆外內存嗎?線程

 咱們一塊兒來看看NIO中提供的ByteBuffer設計

 

 

咱們將最大堆外內存設置成40M,運行這段代碼會發現:程序能夠一直運行下去,不會報OutOfMemoryError。若是使用了-verbose:gc -XX:+PrintGCDetails,會發現程序頻繁的進行垃圾回收活動。那麼DirectByteBuffer到底是如何釋放堆外內存的?指針

咱們修改下JVM的啓動參數,從新運行以前的代碼:對象

 

 

與以前的JVM啓動參數相比,增長了-XX:+DisableExplicitGC,這個參數做用是禁止代碼中顯示調用GC。代碼如何顯示調用GC呢,經過System.gc()函數調用。若是加上了這個JVM啓動參數,那麼代碼中調用System.gc()沒有任何效果,至關因而沒有這行代碼同樣。blog

 

 

顯然堆內存(包括新生代和老年代)內存很充足,可是堆外內存溢出了。也就是說NIO直接內存的回收,須要依賴於System.gc()。若是咱們的應用中使用了java nio中的direct memory,那麼使用-XX:+DisableExplicitGC必定要當心,存在潛在的內存泄露風險

咱們知道java代碼沒法強制JVM什麼時候進行垃圾回收,也就是說垃圾回收這個動做的觸發,徹底由JVM本身控制,它會挑選合適的時機回收堆內存中的無用java對象。代碼中顯示調用System.gc(),只是建議JVM進行垃圾回收,可是到底會不會執行垃圾回收是不肯定的,可能會進行垃圾回收,也可能不會。何時纔是合適的時機呢?通常來講是,系統比較空閒的時候(好比JVM中活動的線程不多的時候),還有就是內存不足,不得不進行垃圾回收。咱們例子中的根本矛盾在於:堆內存由JVM本身管理,堆外內存必需要由咱們本身釋放;堆內存的消耗速度遠遠小於堆外內存的消耗,但要命的是必須先釋放堆內存中的對象,才能釋放堆外內存,可是咱們又不能強制JVM釋放堆內存。

二、Direct Memory的回收機制:

Direct Memory是受GC控制的,例如ByteBuffer bb = ByteBuffer.allocateDirect(1024),這段代碼的執行會在堆外佔用1k的內存,Java堆內只會佔用一個對象的指針引用的大小,堆外的這1k的空間只有當bb對象被回收時,纔會被回收,這裏會發現一個明顯的不對稱現象,就是堆外可能佔用了不少,而堆內沒佔用多少,致使還沒觸發GC,那就很容易出現Direct Memory形成物理內存耗光。

Direct ByteBuffer分配出去的內存其實也是由GC負責回收的,而不像Unsafe是徹底自行管理的,Hotspot在GC時會掃描Direct ByteBuffer對象是否有引用,如沒有則同時也會回收其佔用的堆外內存。   

使用堆外內存與對象池都能減小GC的暫停時間,這是它們惟一的共同點。生命週期短的可變對象,建立開銷大,或者生命週期雖長但存在冗餘的可變對象都比較適合使用對象池。生命週期適中,或者複雜的對象則比較適合由GC來進行處理。然而,中長生命週期的可變對象就比較棘手了,堆外內存則正是它們的菜。

三、堆外內存的好處是:

(1)能夠擴展至更大的內存空間。好比超過1TB甚至比主存還大的空間;

(2)理論上能減小GC暫停時間;

(3)能夠在進程間共享,減小JVM間的對象複製,使得JVM的分割部署更容易實現;

(4)它的持久化存儲能夠支持快速重啓,同時還可以在測試環境中重現生產數據

站在系統設計的角度來看,使用堆外內存能夠爲你的設計提供更多可能。最重要的提高並不在於性能,而是決定性的

相關文章
相關標籤/搜索