運行時數據區(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ī)范