JVM內存模型和GC垃圾回收

JVM 內存區域算法

一、程序計數器.net

  這是一塊較小的內存空間,它的做用能夠看作是當前線程所執行的字節碼的行號指示器,指的是上次代碼被執行的地方,線程私有。線程

二、Java 虛擬機棧指針

  它是 Java方法執行的內存模型,每個方法被調用到執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程,線程私有。對象

三、本地方法棧blog

  跟虛擬機棧相似,不過本地方法棧用於執行本地方法,線程私有。生命週期

四、Java 堆內存

  該區域存在的惟一目的就是存放對象,幾乎應用中全部的對象實例都在這裏分配內存,全部線程共享。字符串

五、方法區get

  它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,全部線程共享。

線程私有的區域隨着線程的結束就沒有了,沒有垃圾回收;gc操做的地方是在全部線程共享的區域。

內存分代


爲何要分代?

       堆內存是虛擬機管理的內存中最大的一塊,也是垃圾回收最頻繁的一塊區域,咱們程序全部的對象實例都存放在堆內存中。給堆內存分代是爲了提升對象內存分配和垃圾回收的效率。試想一下,若是堆內存沒有區域劃分,全部的新建立的對象和生命週期很長的對象放在一塊兒,隨着程序的執行,堆內存須要頻繁進行垃圾收集,而每次回收都要遍歷全部的對象,遍歷這些對象所花費的時間代價是巨大的,會嚴重影響咱們的GC效率,這簡直太可怕了。

       有了內存分代,狀況就不一樣了,新建立的對象會在新生代中分配內存,通過屢次回收仍然存活下來的對象存放在老年代中,靜態屬性、類信息等存放在永久代中,新生代中的對象存活時間短,只須要在新生代區域中頻繁進行GC,老年代中對象生命週期長,內存回收的頻率相對較低,不須要頻繁進行回收,永久代中回收效果太差,通常不進行垃圾回收,還能夠根據不一樣年代的特色採用合適的垃圾收集算法。分代收集大大提高了收集效率,這些都是內存分代帶來的好處。

       內存分代劃分

      Java虛擬機將堆內存劃分爲新生代、老年代和永久代,永久代是HotSpot虛擬機特有的概念,它採用永久代的方式來實現方法區,其餘的虛擬機實現沒有這一律念,並且HotSpot也有取消永久代的趨勢,在JDK 1.7中HotSpot已經開始了「去永久化」,把本來放在永久代的字符串常量池移出。永久代主要存放常量、類信息、靜態變量等數據,與垃圾回收關係不大,新生代和老年代是垃圾回收的主要區域。內存分代示意圖以下:

       

       新生代(Young)

       新生成的對象優先存放在新生代中,新生代對象朝生夕死,存活率很低,在新生代中,常規應用進行一次垃圾收集通常能夠回收70% ~ 95% 的空間,回收效率很高。

       HotSpot將新生代劃分爲三塊,一塊較大的Eden空間和兩塊較小的Survivor空間,默認比例爲8:1:1。劃分的目的是由於HotSpot採用複製算法來回收新生代,設置這個比例是爲了充分利用內存空間,減小浪費。新生成的對象在Eden區分配(大對象除外,大對象直接進入老年代),當Eden區沒有足夠的空間進行分配時,虛擬機將發起一次Minor GC。

       GC開始時,對象只會存在於Eden區和From Survivor區,To Survivor區是空的(做爲保留區域)。GC進行時,Eden區中全部存活的對象都會被複制到To Survivor區,而在From Survivor區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到年齡閥值(默認爲15,新生代中的對象每熬過一輪垃圾回收,年齡值就加1,GC分代年齡存儲在對象的header中)的對象會被移到老年代中,沒有達到閥值的對象會被複制到To Survivor區。接着清空Eden區和From Survivor區,新生代中存活的對象都在To Survivor區。接着, From Survivor區和To Survivor區會交換它們的角色,也就是新的To Survivor區就是上次GC清空的From Survivor區,新的From Survivor區就是上次GC的To Survivor區,總之,無論怎樣都會保證To Survivor區在一輪GC後是空的。GC時當To Survivor區沒有足夠的空間存放上一次新生代收集下來的存活對象時,須要依賴老年代進行分配擔保,將這些對象存放在老年代中。

       老年代(Old)

       在新生代中經歷了屢次(具體看虛擬機配置的閥值)GC後仍然存活下來的對象會進入老年代中。老年代中的對象生命週期較長,存活率比較高,在老年代中進行GC的頻率相對而言較低,並且回收的速度也比較慢。

       永久代(Permanent)

       永久代存儲類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,對這一區域而言,Java虛擬機規範指出能夠不進行垃圾收集,通常而言不會進行垃圾回收。

       Minor GC 和 Full GC的區別

       新生代GC(Minor GC):Minor GC指發生在新生代的GC,由於新生代的Java對象大多都是朝生夕死,因此Minor GC很是頻繁,通常回收速度也比較快。當Eden空間不足覺得對象分配內存時,會觸發Minor GC。

       老年代GC(Full GC/Major GC):Full GC指發生在老年代的GC,出現了Full GC通常會伴隨着至少一次的Minor GC(老年代的對象大部分是Minor GC過程當中重新生代進入老年代),好比:分配擔保失敗。Full GC的速度通常會比Minor GC慢10倍以上。當老年代內存不足或者顯式調用System.gc()方法時,會觸發Full GC。

 垃圾回收算法


  標記-清除算法(Mark-Sweep)
  這是最基礎的收集算法,如它的名字同樣,算法分爲「標記」和「清除」兩個階段:

  首先標記出全部須要回收的對象,在標記完成後統一回收掉全部被標記的對象。

  之因此說它是最基礎的收集算法,是由於後續的收集算法都是基於這種思路並對其缺點進行改進而獲得的。

它的主要缺點有兩個:

  一、效率問題,標記和清除過程的效率都不高;

  二、空間問題,標記清除以後會產生大量不連續的內存碎片,空間碎片太多可能會致使,當程序在之後的運行過程當中須要分配較大對象時沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。

複製算法(Copying)
爲了解決效率問題,一種稱爲「複製」(Copying)的收集算法出現了,它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用過的內存空間一次清理掉。

這樣使得每次都是對其中的一塊進行內存回收,內存分配時也就不用考慮內存碎片等複雜狀況,只要移動堆頂指針,按順序分配內存便可,實現簡單,運行高效。只是這種算法的代價是將內存縮小爲原來的一半,未免過高了一點。

可是這種算法的效率至關高,因此,如今的商業虛擬機都採用這種收集算法來回收新生代。爲何新生代可使用複製算法呢?

IBM 有專門研究代表,新生代中的對象 98% 都是朝生夕死,因此就不須要按照1:1的比例來劃份內存空間。這裏鑑於此,新生代採用了以下的劃分策略。

如今把新生代再劃分爲三部分,一塊較大的 Eden(伊甸園) 和兩塊較小的 Survivor(倖存者) 區域。

當回收時,將 Eden 和 Survivor 中還存活着的對象一次性地拷貝到另一塊Survivor空間上,最後清理掉Eden和剛纔用過的Survivor的空間。HotSpot 虛擬機默認Eden和Survivor的大小比例是8∶1,也就是每次新生代中可用內存空間爲整個新生代容量的90%(80%+10%),只有10%的內存是會被「浪費」的。

這樣清理完成後,原來的 Survivor 就空了,並一直保持爲空,直到下次 Minor GC 時,它再做爲存活對象的盛放地。兩個 Survivor 就這樣輪流當作 GC 過程當中新生代存活對象的中轉站。

可是,若是使用複製算法的內存區域有大量的存活對象時,複製算法就會變得捉襟見肘,這時須要更大的 Survivor 區用於盛放那些存活對象,甚至可能須要 1:1的比例。因此針對堆內存區域的老年代,就有了下面的算法。

標記-整理算法
標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存。這種方法避免了碎片的產生,同時也不須要一塊額外的內存空間,對於老年代會比較合適。

可是相比複製算法,雖然該算法佔用的內存空間少,可是耗費的垃圾回收時間會比複製算法久,因此上面也說了

咱們應該儘可能避免或者減小 Full GC 的發生。

   這兩種算法用精煉的語言描述就是

複製算法:用空間換時間

標記-整理算法:用時間換空間

一句話 魚與熊掌不可兼得,可是針對新生代和老年代,他們都是最佳的選擇。

 

總結
簡單梳理一下文中講到的一些知識點

一、爲了更好的管理堆內存,該區域分爲新生代和老年代。
二、新生代發生垃圾回收要比老年代頻繁。
三、新生代發生的垃圾回收成爲 Minor GC;老年代發生的 GC 成爲 Full GC。
四、新生代使用複製算法進行垃圾回收;老年代使用標記-整理算法
五、爲了更高效管理新生代的內存,按照複製算法,結合 IBM 的研究論證,新生代分爲三塊,一塊比較大的 Eden 區和兩塊比較小的 Survivor 區,比例爲 8:1:1
儘量的避免或者減小垃圾回收

 

本文轉載至:https://blog.csdn.net/sumj7011/article/details/78087421

相關文章
相關標籤/搜索