JAVA内存模型及垃圾回收自我总结
本文為原創,根據《深入理解java虛擬機》和自己的一些理解進行整理,單純和看其他人的博客感覺不如自己一點點的畫和記錄來的印象深刻。
JAVA內存模型:
上圖中:局部變量表所需的內存在編譯期已經分配完成 表達有誤, 準確的表達應該是:局部變量表所需的內存在編譯期就已經計算完成(即需要在運行時分配多大內存)。
判斷對象是否已死(可以回收)的算法
從永久代到年輕代的引用可以被當成 GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉
方法區(永久代)回收的相關說明:
永久代的垃圾回收主要兩部分內容:廢棄的常量和無用的類。廢棄的常量:無任何對象引用此常量。無用的類:1、該類所有實例已經被回收(堆中不存在該類的任何實例)。 2、加載該類的ClassLoader已經被回收。 3、給類對應的Class對象沒有任何地方唄引用,無法在任何地方通過反射訪問該類的方法垃圾收集的算法:
1、標記 - 清除算法
包含標記和清除2個階段,是最基礎的算法。標記就是標記出那些已經可以被回收的對象,在標記完成后進行清楚,此算法效率不高而且會產生內存碎片,當程序在以后的運行中需要分配一塊大的連續的內存時可能因為內存碎片的問題而提前觸發再次垃圾回收。
過程示意圖如下:
2、復制算法(新生代采用的算法之一)
為了解決標記-清除 算法 的效率問題的,將內存劃為多塊,在標記完一塊區域的對象的存活狀態后,將存活的對象拷貝到另外一塊上,然后將之前的那塊一次清理掉。
在新生代上對象存活期很短,并且98%的對象是朝生夕死,所以在內存的劃塊上不需要1:1進行劃分,hotspot虛擬機默認按照Eden-80%,兩塊Survivor各10%進行劃分(8:1),其中Eden和一塊Survivor空間用來存放新生代的對象,還有一塊用來復制垃圾回收時Eden和另外一塊Survivor的存活對象,然后一次清除調用Eden和那塊Survivor內存,下次垃圾回收時這2塊Survivor的功能互相調換。當一塊Survivor不夠裝下存活的對象時,則會向老年代的分配擔保機制(借內存)進入到老年代。
復制算法的示意圖:
?
3、標記 - 整理算法
相比標記-清理算法,此算法解決了標記-清理算法的內存碎片問題,清理完可回收的對象后會對該內存塊進行整理。
算法示意圖:
4、分代收集算法
?當前商業虛擬機都采用此算法,其實說這是算法還不如說是一個將前面3個算法在新生代和老年代上應用不同算法的一個策略,新生代采用復制算法,老年代采用“標記-清除”或者“標記-整理”算法。
垃圾回收器(垃圾收集算法的具體實現)
HotSpot 1.6版Update22的收集器入下所示:
上面部分是新生代的垃圾回收器,下面部分是老年代的垃圾回收器,中間的連線表示這2個回收器可以配合一起使用。
?新生代的垃圾收集器
1、Serial收集器(復制算法)
此收集器是最基本的,也是歷史最悠久的收集器,是JDK1.3.1之前新生代的唯一選擇,該收集器的缺點是垃圾收集器回暫停用戶的所有線程來進行單線程的垃圾收集,后面的其它收集器基本就是在此收集器的基礎進行優化修改而來,在Client模式下運行還是可以的接受的。
運行示意圖如下:
2、ParNew收集器(復制算法)
Serial的多線程版本,是許多運行在Server模式下的虛擬機中新生代收集器的首選。在多核心的服務器上運行優勢明顯,默認開啟的線程=CPU核心數,
通過使用-XX:ParallelGCThreads參數可以來限制垃圾收集的線程數。
運行示意圖:
3、Parallel Scavenge收集器(復制算法)
跟parNew收集器差不多,但與上面2個收集器的關注點不同,前面的2個更多的關注用戶的停頓時間,而此收集器更關注CPU的吞吐量(吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)),
其中有2個參數可以控制這個吞吐量,1、-XX:MaxGCPauseMillis xxxx,控制最大垃圾收集停頓時間(x>0的毫秒數)。2、-XX:GCTimeRatio XXX,設置吞吐量大小(設置一個0<x<100的整數,表示垃圾收集器時間占總時間的比率,相當于吞吐量的倒數)。
另外,此收集器還有一個參數-XX:+UseAdaptiveSizePolicy,如果打開這個開發,則新生代的大小(-Xmn)、Eden、Survivor(-XX:SurvivorRatio)、晉升老年代的對象年齡(-XX:PretenureSizeThreshold)等參數就不需要手工指定了,虛擬機會根據系統的運行情況來動態調整。
運行示意圖:
老年代收集器
1、Serial Old收集器(標記-整理算法)
新生代serial的老年代版本,主要有如下2個用途:1、在JDK1.5以及之前的版本搭配新生代的Parallel Scavenge收集器使用。2、作為CMS收集器發生Concurrent Mode Failure的一個后備方案。
2、Parallel Old收集器(標記-整理算法)
新生代Parallel Scavenge收集器的老年代版本,在JDK1.6的版本中才提供,只能和新生代的Parallel Scavenge收集器搭配使用。
3、CMS收集器(標記-清除算法)
Concurrent Mark Sweep,一種以獲取最短回收停頓時間為目標的收集器,具有并發低頓挫的特點,但其也有幾個缺點:
①對CPU資源很敏感(需要跟用戶線程并發執行)。
②無法處理浮動垃圾(Floating Garbage,在標記過程后產生的垃圾),CMS需要預留足夠的內存空間給用戶線程使用,所以CMS收集器在老年代使用了68%的空間后被激活(可以通過-XX:CMSInitiatingOccupancyFranction參數來配置觸發百分比),如果在運行過程中預留的內存無法滿足程序需要,則會出現一次“Concurrent Mode Failure”失敗并啟用后備預案(Serial Old收集器)進行Full GC。
③由于采用標記-清除算法,會產生內存碎片,不過可以配置-XX:+UseCMSCompactAtFullCollection開關讓Full GC后進行一次碎片整理,也可以使用-XX:CMSFullGCsBeforeCompaction來設置執行多少次Full GC自動來一次碎片整理。
共分為4個過程:
㈠、初始標記
需要暫停用戶線程,但過程很快。
㈡、并發標記
㈢、重新標記
需要暫停用戶線程,對前面的標記進行修正。
㈣、并發清除
運行示意圖:
4、G1收集器
Garbage First收集器是當前收集器技術發展的前沿成果,在JDK1.6_update14提供試用,是一款面向服務器應用的垃圾收集器。
主要有如下特點:
● 并發運行
● 基于標記-整理算法,不會產生空間碎片
● 非常精確的控制停頓,能讓使用者明確指定在一個長度為M毫秒的時間片段內,消耗在收集上的時間不超過N毫秒,這幾乎是實時JAVA(RTSJ)垃圾收集器的特征了
● 將整個堆(新生代、老年代)劃分為多個大小固定的獨立區域,并與這些區域為單位進行垃圾回收,避免對整個堆進行全量回收操作,并對這些區域進行優先級維護和回收。
?
進入老年代的幾種途徑
1、在新生代GC過程中survivor空間不夠,通過老年代的分配擔保機制提前轉入老年代。
2、超過參數PretenureSizThreshold設置的值的大對象直接進入老年代,所謂大對象就是需要大量連續內存空間的java對象(例如很長的字符串和大數組)。
3、長期存活的新生代對象進入老年代,如果對象在一次Minor GC(新生代GC)后仍存活并且能被survivor容納后被移動到Survivor中后將該對象的年齡+1,當它的年齡達到一定程 ? ? 度(默認15)后就會晉升到老年代,這個閥值可以通過-XX:MaxTenuringThreshold來設置。
4、虛擬機并不總要求對象達到XX:MaxTenuringThreshold的值后才晉升老年代,如果在Survivor空間中相同年齡所有對象大小總和大于Survivor空間的一半,年齡大于或者等于 ? ? ?該年齡的對象就可以直接進入老年代。
?
總結
以上是生活随笔為你收集整理的JAVA内存模型及垃圾回收自我总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发技巧(3-1)Eclipse查找关键
- 下一篇: linux 下/etc/profile、