JAVA堆外內存


    JVM可以使用的內存分外2種:堆內存和堆外內存.

    堆內存完全由JVM負責分配和釋放,如果程序沒有缺陷代碼導致內存泄露,那么就不會遇到java.lang.OutOfMemoryError這個錯誤。

    使用堆外內存,就是為了能直接分配和釋放內存,提高效率。JDK5.0之后,代碼中能直接操作本地內存的方式有2種:使用未公開的Unsafe和NIO包下ByteBuffer。

    關於Unsafe對象的簡介和獲取方式,可以參考:http://blog.csdn.net/aitangyong/article/details/38276681

    使用ByteBuffer分配本地內存則非常簡單,直接ByteBuffer.allocateDirect(10 * 1024 * 1024)即可。

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

    我們一起來看看NIO中提供的ByteBuffer

      我們將最大堆外內存設置成40M,運行這段代碼會發現:程序可以一直運行下去,不會報OutOfMemoryError。如果使用了-verbose:gc -XX:+PrintGCDetails,會發現程序頻繁的進行垃圾回收活動。那么DirectByteBuffer究竟是如何釋放堆外內存的?

    我們修改下JVM的啟動參數,重新運行之前的代碼:

      與之前的JVM啟動參數相比,增加了-XX:+DisableExplicitGC,這個參數作用是禁止代碼中顯示調用GC。代碼如何顯示調用GC呢,通過System.gc()函數調用。如果加上了這個JVM啟動參數,那么代碼中調用System.gc()沒有任何效果,相當於是沒有這行代碼一樣。

      顯然堆內存(包括新生代和老年代)內存很充足,但是堆外內存溢出了。也就是說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對象是否有引用,如沒有則同時也會回收其占用的堆外內存。

參考:http://blog.csdn.net/aitangyong/article/details/39403031

          http://blog.csdn.net/aitangyong/article/category/2159887

          http://blog.csdn.net/aitangyong/article/details/39323125

          http://hellojava.info/?p=56

          http://hellojava.info/?tag=maxdirectmemorysize

          https://yq.aliyun.com/articles/2948

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

堆外內存的好處是:

(1)可以擴展至更大的內存空間。比如超過1TB甚至比主存還大的空間;

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

(3)可以在進程間共享,減少JVM間的對象復制,使得JVM的分割部署更容易實現;

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

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

堆外內存說明請參見:http://ju.outofmemory.cn/entry/200574

                                    http://blog.csdn.net/aitangyong/article/category/2159887


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM