1. 程式人生 > >你應該這樣理解JVM內存管理

你應該這樣理解JVM內存管理

進行 根據 nor c++ 復制 問題 term 來看 垃圾

在進行Java程序設計時,一般不涉及內存的分配和內存回收的相關代碼,此處引用一句話: Java和C++之間存在一堵由內存動態分配和垃圾收集技術所圍成的高墻,墻外的人想進去,墻裏面的人想出來 ,個人從這兩句話中,捕獲到了 兩個點 。

java的自動內存管理機制,極大的節省了開發人員的精力,避免了易錯且復雜的內存管理設計,相對於手動的內存管理這是極大的飛躍。
java自動內存管理機制,其不能根據具體的場景提供最優的內存管理,其只提供普適的內存管理機制。想比於C++的手動內存管理,靈活性不夠,存在制約系統性能的瓶頸。
第二點是我們深入了解JVM內存管理機制的意義,通過對原理的把握,在指定的場景下設計JVM最優的內存管理策略,本文內容組織結構如下:

JVM內存分配
JVM內存回收

JVM內存分配
技術分享圖片

ClassExample refereenceExample = new ClassExample ();
復制代碼
上述代碼,如果粗略劃分的話,可以劃分為兩個過程:

在堆區分配對象內存
將對象內存地址賦值給對象引用 上述兩個過程基本就是內存分配中,比較重要的兩個知識點了, 內存分配策略和對象引用 #####2.1 堆區布局 對象分配在堆上這是毫無疑問的,如果在往下細分的話,那麽堆區的內存布局還是挺有講究的,大致可以分為如下布局:
技術分享圖片

新生代:從名字可見一般,新生代區域中存放的對象一般是兩種: 剛剛被new出來的對象,或者經歷內存回收次數不多的對象 。
老年代:老年代從名字也可見一般,一般存放兩種對象: 占用內存比較多的大對象和經歷過多次內存回收過程的對象

永久代:永久代的內存就比較固定,每個類的class對象,一般存放在永久代當中。
上述JVM堆區中的內存布局代表的是 邏輯視圖 ,並不是實際的物理布局,實際上,JVM了提供多種不同的內存分配和回收的策略,每種策略抽閑出邏輯視圖都會有細微的差別,但是上述邏輯視圖可以說是所有邏輯視圖的根視圖

2.2 內存分配一般過程
技術分享圖片

在圖中有幾個重要的概念,需要著重強調:

對象大小閾值設定 ,在JVM中可以通過設PretenureSizeThreshold這個參數,指定該閾值大小,如果待分配對象大小超過該閾值,嘗試在老年代中開辟內存空間存儲對象,否則嘗試在新生代中開辟內存空間存儲對象
MinorGC ,這是JVM中一次內存回收過程,內存回收過程又稱垃圾回收過程, 這次內存回收是由新生代中內存空間不足引起的,主要對新生代中的內存對象進行回收

FullGC ,這也是JVM中的一次內存回收過程, 這次內存回收過程是有老年代中內存空間不足造成的,主要針對所有堆區中的內存對象進行回收,包括新生代,老年代,以及永久代
對象年齡 ,經過一次內存回收後依然存活的對象,其年齡會加1。當對象年齡超過一個指定閾值後,其由新生代轉向老年代存儲。這個對象年齡的閾值,同樣可以通過設置JVM的MaxTenuringThreshold參數進行指定

2.3對象引用

JVM內存區域的布局詳情 中介紹了對象引用相關的內容

3. JVM內存回收

從程序員角度來看,內存回收的過程是透明,具體細節都對程序員屏蔽了。 JVM內存區域的布局詳情 ,仔細的介紹了JVM中的內存模型以及各個內存區存儲的數據類型。
技術分享圖片

內存回收主要針對的內存區域主要是堆區和方法區,在上文中談及了MinorGC以及FullGC。MinorGC主要是針對堆區進行內存回收,FullGC除了對堆區進行回收,對方法區也進行回收,是相對重量級的回收動作。在內存回收這個章節中,主要從以下三個方面闡述: 1. 什麽樣的對象可回收?解決方法:可達性分析 2. 如何回收內存? 解決方法:標記-清除算法,標記-復制算法,標記-整理算法等內存回收算法 3.什麽時候回收合適? 解決方法:安全點和安全區域3.1 可達性分析
技術分享圖片

在上圖中,一共有七個對象,箭頭的走向代表引用關系、對象1引用對象2,對象2引用對象3。 對象1是根節點(GCROOT),其他的是非根節點(GCROOT)。 可達性分析是指:不能被GCROOT節點通過引用鏈達到的節點,將被列入擬回收對象範圍。對象6和對象7就不能被任何一個GCRoot節點所引用,是目標回收對象。JVM認為這些對象不具備使用價值,可以將其進行內存回收。 根節點(GCROOT)是上文中提出的重要概念,一般來說,如下對象可作為根節點對象: (1)JVM虛擬機棧中引用的對象 (2)方法區中類靜態屬性引用對象 (3)方法區中類常量引用對象 (4)本機方法棧中引用對象 可達性分析是進行內存回收的判定條件,在可達性分析之後,確定哪些對象是回收目標。除了可達性分析之外, 引用計數法同樣可用來進行判斷內存回收目標對象,但是其無法解決循環引用問題。主流的JVM都采用可達性分析。

3.2內存回收算法

通過可達性分析之後,將確定哪些對象是回收目標,接著內存回收算法將執行具體的回收細節。下圖是內存區域中三個狀態,空閑內存是未使用內存,目標回收內存是可達性分析之後可回收內存,已占內存是不需要回收內存。
技術分享圖片

標記-清除算法 標記-清除算法是比較直白的內存回收算法,其直接釋放目標回收內存,沒有其他任何附加操作。該算法執行後的結果如下:
技術分享圖片

標記-清除算法,邏輯簡單,易於理解,但是其有個致命的缺點就是其極易產生內存碎片,在分配大對象時,不能有足夠連續的內存空間而造成頻繁的GC。
標記-復制算法 標記-復制算法是針對內存碎片問題提出的一種算法。
技術分享圖片

在標記-復制算法的設定中,始終有塊空白內存區域,如上圖長內存區域二。在內存回收時,將內存區域一種存活對象全部按序復制到內存區域二中後再對內存區域一中所有對象進行回收。通過這種做法可以極大的避免內存碎片,但是空白內存區域將始終閑置,內存利用率不高。 在上文中提及將堆區的內存分為兩個surivior區以及Eden區就是采用標記-復制算法。Eden區和surivior之間大小比例分配默認是8:1:1.
標記-整理算法 標記-整理算法,同樣是為解決內存碎片提出的內存回收算法,其在標記-清楚算法的基礎上增加的整理功能,對內存對象進行拷貝移動,以保證空間內存空間連續。
技術分享圖片

標記-整理算法,能夠克服內存碎皮,且不需要額外內存,但是其拷貝和移動對象的時間消耗較高。該算法常用在老年代對象回收中。
3.3 什麽時候回收內存合適

通過可達性分析,可知哪些對象是需要回收的對象,在這過程中需要枚舉根節點,這是一個耗時操作,為了保證內存回收的順利進行,必須保證引用關系的一致性,即在內存回收過程中對象引用關系不會發生變化。只有在這些地方進行內存回收才是安全點,因此JVM引入了 安全點 的概念,安全點處的對象引用關系不會改變,適合內存回收。 安全區域 是對 安全點概念的擴充 ,指在這一段區域內對象引用關系具有一致性,能進行內存回收。

喜歡的朋友點點關註
原文出處:來源於網絡

你應該這樣理解JVM內存管理