JVM的內存結構、垃圾回收算法和垃圾收集器的詳解

運行時數據區(qū)(Run-Time Data Areas)

運行時數據區(qū)
  • 本地方法區(qū):方法區(qū)和堆一樣,也是被所有線程共享的內存區(qū)域。它存儲已被虛擬機加載的類信息,常量,靜態(tài)變量,即時編譯后的代碼數據。
  • 虛擬機棧(JVM Stacks):每個Java虛擬機線程都有一個私有Java虛擬機棧,與線程同時創(chuàng)建。每個方法執(zhí)行的同時都會創(chuàng)建一個棧,用于存儲局部變量表,操作數棧,動態(tài)鏈接,方法出口等信息,每一個方法從調用到直至完成的過程,對應著一個棧在虛擬機中入棧到出棧的過程。
  • 本地方法棧:與虛擬機棧發(fā)揮的作用類似,他們之間的區(qū)別是:虛擬機棧是為虛擬機執(zhí)行Java方法提供服務;本地方法棧是為虛擬機執(zhí)行native方法服務。
  • 堆(Heap ):堆是所有線程之間共享的內存區(qū)域。堆是運行時數據區(qū),從中分配所有類實例和數組的內存。堆是在虛擬機啟動時創(chuàng)建的。
  • 程序計數器(PC Register):JVM支持多線程同時執(zhí)行,每個線程都有自己的PC Register,線程正在執(zhí)行的方法叫當前方法。如果該方法不是 native方法,則PC Register包含當前正在執(zhí)行的Java虛擬機指令的地址。如果線程當前正在執(zhí)行該方法是native方法,則Java虛擬機PC Register的值未定義。
  • 運行時常量池:是方法區(qū)的一部分。Class文件除了版本,字段,方法,接口等描述信息外,還有一項信息是常量池,用于存放編譯期生成的字面量和符號的引用,這部分內容將在類加載后進入方法區(qū)的運行時常量池存放。

Java的內存結構

JVM內存結構

常用JVM配置參數

  • -Xms jvm初始的堆內存大小,默認為物理內存的1/64,等價于 -XX:InitialHeapSize=
  • -Xmx jvm最大的堆內存大小,默認為物理內存的1/4,等價于 -XX:MaxHeapSize=
  • -Xss 設置單個線程棧的大小,默認512k-1024k,等價于 -XX:ThreadStackSize
  • -Xmn 設置新生代的大小,默認為堆空間的1/3
  • -XX:MetaSpaceSize:設置元空間的大小,元空間本質和永久代類似,都是對JVM規(guī)范中本地方法區(qū)的實現。不過元空間與永久代最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內存,因此默認情況下,元空間的大小僅受物理內存大小的限制。
  • -XX:+PrintGCDetails 輸出詳細GC收集日志信息
  • -XX:SurvivorRatio:設置新生代中eden(survivorFrom)和S0/S1(survivorTo)的比例
    默認-XX:SurvivorRatio=8,Eden:S0:S1=8:0:0
    SurvivorRatio的值就是默認eden的比例占比,S0和S1占比相同
  • -XX:NewRatio:配置新生代和老年代在堆中空間大小的比例
    默認-XX:NewRatio=2,新生代占1,老年代占2,新生代占整個堆內存的1/3
    假如-XX:NewRatio=4,新生代占1,老年代占4,新生代占整個堆內存的1/5
    NewRatio就是設置老年代的占比值,剩下的1給新生代
  • -XX:MaxTunuringThreshold 設置垃圾的最大年齡,java8默認為15,如果設置值的話必須在0-15如果設置為0,則年輕代不經過Survivor區(qū),直接進入年老代,對于老年代比較多的應用,可以提高效率如果設置的的比較大,則年輕代會在Survivor區(qū)進行多次復制,這樣增加 對象在年輕代的存活時間,增加在年輕代即被回收的概率。

垃圾回收算法

1. 什么是垃圾?

簡單來說就是內存中不可能再被使用到的對象空間就是垃圾。

2 如何確定一個對象是垃圾?可以回收

  • 引用計數法:如果一個對象的引用為0,則可以回收。但是如果兩個對象相互引用,則這兩個對象永遠不可回收,所以現在基本不使用該方法判斷。
  • 枚舉根節(jié)點,可達性分析,Java虛擬機采用的算法?;舅悸肪褪峭ㄟ^一系列名為“GC Roots”的對象作為起始點,開始向下搜索,如果一個對象到GC Roots沒有任何引用鏈相連時,則說明此對象不可用。也即,給一個集合的引用為根出發(fā),通過引用關系遍歷圖,能被遍歷到的就判定為存活,沒有被遍歷到的自然判定為死亡。
Java中可以作為GC Roots對象有:
  • 虛擬機棧(棧幀中的局部變量區(qū),也叫局部變量表)中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象。
  • 方法區(qū)中常量引用的對象。
  • 本地方法棧中Native方法引用的對象。
  • 線程

3. 常用的垃圾回收算法

復制算法

java堆從GC角度還可以細分為:新生代(Eden區(qū),From Survivor區(qū)和To Survivor區(qū),占用1/3的堆空間)和老年代(占據2/3的堆空間)。

Minitor GC的過程(復制-清空-互換)
Eden、SurvivorFrom復制到SurvivorTo,年齡+1;

  • 首先,當Eden區(qū)滿的時候,第一次觸發(fā)GC,把還活著的對象拷貝到SuvivorFrom區(qū),當Eden區(qū)滿再次觸發(fā)GC的時候會掃描Eden區(qū)和SurvivorFrom區(qū),對這兩個區(qū)域進行垃圾回收,經過這次回收還活著的對象,則直接復制到SuvivorTo區(qū)域(如果對象的年齡達到了老年代,則賦值到老年代),同時把這些對象的年齡+1;
  • 清空Eden和SurvivorFrom中的對象,也即復制之后交換,誰空誰是To;
  • SurvivorFrom和SurvivorTo互換
  • 最后,SurvivorFrom和SurvivorTo互換,原SurvivorTo成為下一次GC時的SurvivorFrom區(qū),部分對象會在From區(qū)域和To區(qū)域復制來復制去,如此交換15次(由JVM參數MaxTenuringThreshold決定,這個參數默認是15)
  • 最終如果還是存活,就存入老年代。
    優(yōu)點:整體復制,沒有產生內存碎片。
    缺點:浪費空間,大對象復制會耗時。
標記清除

先標記出要回收的對象,再統一回收這些要清除的對象。

  優(yōu)點:沒有大面積的復制,節(jié)約內存空間
  缺點:產生內存碎片。
標記整理

標記要回收的對象,再次掃描,并往一端滑動存活的對象。

  優(yōu)點:沒有內存碎片,可以使用bump.
  缺點:移動存活對象的成本,耗時間。

主流的三種算法各有優(yōu)缺點,綜合來看,使用分代收集,復制算法用于年輕代,標記清除,標記整理用于老年代。

GC垃圾回收算法和垃圾收集器的關系?

GC算法(引用計數,復制,標記整理,標記清除)是內存垃圾回收的方法論。垃圾收集器就是GC算法的落地實現。

因為目前為止還沒有出現完美的垃圾回收器出現,更加沒有萬能的收集器,只有針對具體的應用最合適和垃圾回收器,進行分代收集。

4種主要的垃圾收集器
  • serial 串行垃圾回收器:他為單線程環(huán)境設計且只使用一個線程進行垃圾回收,會暫停所有的用戶線程所以不適合服務器環(huán)境。
  • paraller 并行垃圾回收器:多個垃圾收集線程并行工作,此時用戶的線程是暫停的,適用于科學計算,大數據處理首臺處理等弱交互的場景。
  • CMS 并發(fā)垃圾回收器:用戶線程和垃圾回收線程同時執(zhí)行(不一定是并行,有可能交替進行),不需要停頓用戶線程互聯網公司多使用他,對于響應有時間限制的場景。
  • G1垃圾回收器:將堆內存分割為不同的區(qū)域,然后并發(fā)的對其進行垃圾回收。
如何選用合適的垃圾收集器?

組合的選擇:
1、單核的CPU或者小內存,單機程序
-XX:UseSerialGC

2、多CPU,需要最大吞吐量,如后臺計算型應用
-XX:UseParallelGC 或者-XX:UseParallelOldGC

3、多CPU,追求最小停頓時間,最快速響應,如互聯網應用
-XX:+UserConcMarkSweepGC
-XX:+ParNewGC

參考鏈接:JVM官方規(guī)范

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容