1. 程式人生 > >JAVA堆內記憶體、堆外記憶體

JAVA堆內記憶體、堆外記憶體

定義

堆記憶體

完全由JVM負責分配和釋放,如果程式碼有程式缺陷,可能是觸發OOM

堆外記憶體

為了能直接分配和釋放記憶體,提高效率。使用方式:使用未公開的Unsafe和NIO下的ByteBuffer

堆外記憶體的回收機制

Direct Memory是受GC控制的,例如ByteBufferbb = ByteBuffer.allocateDirect(1024),這段程式碼的執行會在堆外佔用1k的記憶體,Java堆內只會佔用一個物件的指標引用的大小,堆外的這1k的空間只有當bb物件被回收時,才會被回收,這裡會發現一個明顯的不對稱現象,就是堆外可能佔用了很多,而堆內沒佔用多少,導致還沒觸發GC,那就很容易出現Direct Memory造成實體記憶體耗光。

DirectByteBuffer分配出去的記憶體其實也是由GC負責回收的,而不像Unsafe是完全自行管理的,Hotspot在GC時會掃描DirectByteBuffer物件是否有引用,如沒有則同時也會回收其佔用的堆外記憶體。   

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

NIO直接記憶體的回收,需要依賴於System.gc()。如果我們的應用中使用了java nio中的directmemory,那麼使用-XX:+DisableExplicitGC一定要小心,存在潛在的記憶體洩露風險

堆記憶體由JVM自己管理,堆外記憶體必須要由我們自己釋放;堆記憶體的消耗速度遠遠小於堆外記憶體的消耗,但要命的是必須先釋放堆記憶體中的物件,才能釋放堆外記憶體,但是我們又不能強制JVM釋放堆記憶體。

堆外記憶體的好處

可以擴充套件至更大的記憶體空間。比如超過1TB甚至比主存還大的空間;

理論上能減少GC暫停時間;

可以在程序間共享,減少JVM間的物件複製,使得JVM的分割部署更容易實現;

它的持久化儲存可以支援快速重啟,同時還能夠在測試環境中重現生產資料