讀深入理解Java虛擬機筆記--JVM垃圾回收

? 最近開始讀周志明老師《深入理解Java虛擬機》,已經將第二部分的自動內存管理機制詳細的讀了一遍,對java虛擬機的內存模型,垃圾回收有了初步的了解。

一、JVM內存區域

? Java虛擬機在運行時,會把內存空間分為若干個區域,根據《Java虛擬機規范(Java SE 7 版)》的規定,Java虛擬機所管理的內存區域分為如下部分:方法區、堆內存、虛擬機棧、本地方法棧、程序計數器。

1.程序計數器

? 程序計數器(Program CounterRegister)是一塊較小的內存,它可以看做是當前線程所執行字節碼的行號指示器。為了線程切換后能恢復到正確的執行位置,每個線程都需要有一個單獨的程序計數器,各線程之間計數器互不影響,獨立存儲,這類內存區域可以稱為“線程私有”的內存。此內存區域是唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。

2.Java虛擬機棧

? 與程序計數器一樣,Java虛擬機棧(Java Virtual MachineStacks)也是線程私有的,它的生命周期與線程相同。虛擬機會為每個線程分配一個虛擬機棧,每個虛擬機棧中都有若干個棧幀,每個棧幀中存儲了局部變量表、操作數棧、動態鏈接、返回地址等。一個棧幀就對應Java代碼中的一個方法,當線程執行到一個方法時,就代表這個方法對應的棧幀已經進入虛擬機棧并且處于棧頂的位置,每一個Java方法從被調用到執行結束,就對應了一個棧幀從入棧到出棧的過程。

3.本地方法棧

? 本地方法棧(Native MethodStacks)與虛擬機棧所發揮的作用是非常相似的。他們的區別不過是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則為虛擬機執行Native方法服務。

4.java堆

? 對大多數應用來說,Java堆是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建,此內存區域的唯一目的就是存放實例對象,幾乎所有的對象實例都在這里分配內存。從內存回收的角度看,由于現在收集器基本都采用分代收集算法,所以Java堆可以細分為:新生代和老年代;再細致一點的有Eden空間、From Survivor空間、To Survivor空間等。從內存分配的角度看,線程共享的Java堆中可能劃分出多個線程私有的分配緩沖區(Thread? Local Allocation Buffer,TLAB)。

5.方法區

? 方法區(Method Area)與Java堆一樣是各線程共享的內存區域,它用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。雖然Java虛擬機把方法區描述為堆的一個邏輯部分,但它卻有一個別名叫做Non-heap(非堆),目的應該是與Java堆區分開來。從jdk1.7已經開始準備“去永久代”的規劃,jdk1.7的HotSpot中,已經把原本放在方法區中的靜態變量、字符串常量池等移到堆內存中,(常量池除字符串常量池還有class常量池等),這里只是把字符串常量池移到堆內存中;在jdk1.8中,方法區已經不存在,原方法區中存儲的類信息、編譯后的代碼數據等已經移動到了元空間(MetaSpace)中,元空間并沒有處于堆內存上,而是直接占用的本地內存(NativeMemory)。

二、JVM垃圾回收

? 垃圾回收,就是通過垃圾收集器把內存中沒用的對象清理掉。垃圾回收涉及到的內容有:1、判斷對象是否存活;2、垃圾收集算法;3.垃圾回收器。

1 判斷對象是否存活

(1)引用計數法

? 引用計數法是指給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時候計數器值為0就表示不會再被任何對象使用。引用計數法(Reference Counting)的實現簡單,判斷效率也很高,在大部分情況下都是一個不錯的算法。但在主流的Java虛擬機里面沒有使用引用計數法來管理內存,主要原因是它很難解決對象之間相互循環引用的問題。

? 如圖:當引用1和引用2斷開后,對象1和對象2之間彼此引用,這時候兩個對象的引用計數器不為0,無法判斷他們是死對象,垃圾回收器也就無法回收。


(2)可達性分析法

? 基本思路是通過一系列稱為GC Roots的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈(就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。

? 在Java語言中,可作為GC Roots的對象包括下面幾種:

??? ①虛擬機棧(棧幀中的本地變量表)中引用的對象。

??? ②方法區中類靜態屬性引用的對象。

??? ③方法區中常量引用的對象。

? ? ④本地方法棧中JNI(即Native方法)引用的對象。

? 進行可達性分析后對象和GC Roots之間沒有引用鏈相連時,對象將會被進行一次標記,接著會判斷如果對象沒有覆蓋Object的finalize()方法或者finalize()方法已經被虛擬機調用過,那么它們就會被行刑(清除);如果對象覆蓋了finalize()方法且還沒有被調用,則會執行finalize()方法中的內容,所以在finalize()方法中如果重新與GC Roots引用鏈上的對象關聯就可以拯救自己。但是周志明老師也建議盡量不用使用這個方法,因為它運行代價高昂,不確定性大,無法保證各個對象的調用順序,并不適合做“關閉外部資源”之類的工作。

(3)對象引用

? 在JDK1.2之后,Java對引用的概念做了擴充,將引用分為強引用(StrongReference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(PhantomReference)4種,這4種引用的強度依次減弱。

2 垃圾收集算法

2.1 標記-清除算法

? “標記-清除”(Mark-Sweep)算法是最基礎的收集算法,這個算法分為“標記”和“清除”兩個階段:首先標記出需要回收的所有對象,在標記完成后統一回收標記的對象,標記過程就是之前講的通過引用計數法和可達性分析法進行判定。之所以說它是最基礎的算法,是因為后面的算法都是在它基礎上進行的改進,它的主要不足有兩個:一個是效率問題,標記和清除的效率都不高,還有一個是空間問題,標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能會導致在需要分配較大對象時,無法找到連續的內存空間而不得不提前觸發另一次垃圾收集動作。如圖:


標記-清除示例圖

2.2 復制算法

? 把內存分為大小相等的兩塊,每次存儲只用其中一塊,當這一塊用完了,就把存活的對象全部復制到另一塊上,同時把使用過的這塊內存空間全部清理掉,往復循環。現在商用虛擬機都采用這種算法來回收新生代,因為新生代中98%的對象都是朝生夕死的,所以并不需要1:1的比例來劃分內存空間,而是將內存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和一塊Survivor空間。當回收時,將Eden和Survivor中開存活的對象一次性復制到另一塊Survivor空間上,最后清理掉Eden和剛才使用過的Survivor空間。HotSpot虛擬機默認Eden和Survivor的大小是8:1,也就是每次新生代可用空間是整個新生代容量的90%(80%+10%),只有10%的內存會被“浪費”。當然,98%可回收只是一般的情況的數據,我們沒有辦法保證每次回收都只有不多于10%的對象存活,當Survivor空間不夠用時,需要依賴其他內存(這里指老年代)進行分配擔保(Handle Promotion)。

2.3 標記-整理算法

? 標記過程任然與“標記-清除”算法的標記過程相同,但后續步驟不是對可回收對象進行直接清理,而是讓所有活的對象都移動到另一端,然后直接清理掉端邊界以外的內存,這種算法適合于老年代。

2.4 分代收集算法

? 把堆內存分為新生代和老年代,新生代又分為Eden區、From Survivor和To Survivor。一般新生代中的對象基本上都是朝生夕滅的,每次只有少量對象存活,因此采用復制算法,只需要復制那些少量存活的對象就可以完成垃圾收集;老年代中的對象存活率較高,就采用標記-清除和標記-整理算法來進行回收。

3 垃圾收集器

? 現在常見的垃圾收集器有如下幾種

? 新生代收集器:Serial、ParNew、Parallel Scavenge

? 老年代收集器:Serial Old、CMS、Parallel Old

? 堆內存垃圾收集器:G1

3.1 Serial收集器

? Serial收集器是最基本、發展歷史最悠久的收集器。這個收集器是單線程收集器,采用復制算法進行垃圾收集,它在完成垃圾收集工作時,必須暫停其他所有的工作線程,直到它收集結束。就好比“在打掃房間時你必須老老實實的在椅子上或者房間外待著,如果一邊打掃,你一邊亂扔紙屑,那么房間就打掃不完了?!?,這確實是一個合乎情理的矛盾,雖然垃圾收集這項工作聽起來和打掃房間屬于一個性質,但實際上肯定是要比打掃房間復雜的多。

3.2 ParNew收集器

? ParNew收集器實際上就是Serial收集器的多線程版本,但它卻是許多運行在Server模式下虛擬機的首選新生代收集器,其中有一個與性能無關但很重要的原因是,除了Serial收集器之外,目前只有ParNew收集器能與CMS收集器配合工作。

3.3 ParalleScavenge收集器

? Paralle Scavenge收集器是一個新生代收集器,它也是使用復制算法的收集器,又是并行的多線程收集器。他的特點是它的關注點與其他收集器不同,CMS收集器的關注點是盡可能地縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目的是達到一個可控制的吞吐量(Throughout)。所謂吞吐量就是CPU運行用戶代碼的時間與CPU總消耗時間的比,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總共運行了100分鐘,其中垃圾收集花掉了1分鐘,那吞吐量就是99%。

3.4 SerialOld收集器

? Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用“標記-整理”算法。這個收集器的主要意義也是給Client模式的虛擬機使用。如果在Server模式下,那么它有兩大用途:一種用途是在JDK1.5以及以前的版本中與Parallel Scavenge收集器搭配使用,另一種用途是作為CMS收集器的后備方案,在并發收集發生Concurrent Mode Failure時使用。

3.5 ParallelOld收集器

? Parallel Old是Parallel Scavenge收集器的老年代版,使用多線程和“標記-整理”算法。

3.6 CMS收集器

? CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。目前很大一部分Java應用集中在互聯網站或者B/S系統的服務端上,這類應用尤其注重服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。CMS收集器就非常符合這類應用的需求。

? 整個垃圾收集過程分為4個步驟:

? ① 初始標記:標記一下GC Roots能直接關聯到的對象,速度較快

? ② 并發標記:進行GC Roots Tracing,標記出全部的垃圾對象,耗時較長

? ③ 重新標記:修正并發標記階段引用戶程序繼續運行而導致變化的對象的標記記錄,耗時較短

? ④ 并發清除:用標記-清除算法清除垃圾對象,耗時較長

3.7 G1收集器

G1是一款面向服務器的垃圾收集器。HotSpot開發團隊賦予它的使命是未來可以替換掉JDK1.5中發布的CMS收集器

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 讓無邊的天空永遠湛藍~ 讓無垠的大海永遠寬廣~ 讓你的笑容永遠留住~ 不然我會傷心哭泣…… 不用環視四周,這樣就可...
    晴風村閱讀 87評論 0 0
  • 減肥好累。
    溫純丨丨閱讀 122評論 0 0
  • 千年一餅圓月, 一直照到天涯; 誰的思念滴落? 浸濕一樹桂花。
    孤獨乞丐閱讀 97評論 0 0
  • 你在最南的海邊滌蕩 風自南拂面 面北你浮游 幽城鐘聲老 放你一人空 靜山景行遠 任你孤身依 你在最南的海邊隱匿 風...
    水遼原閱讀 236評論 0 0