G1垃圾回收器詳解

在前一篇的文章《HotSpot垃圾回收算法概述》里面,對于Serial, Parallel和CMS幾種垃圾回收器做了比較詳細的描述。但是對于G1的敘述是比較粗糙的。這篇文章則是提供了G1垃圾回收器的詳細分析。

概述

G1垃圾回收器是在Java7 update 4之后引入的一個新的垃圾回收器。G1是一個分代的,增量的,并行與并發(fā)的標記-復(fù)制垃圾回收器。它的設(shè)計目標是為了適應(yīng)現(xiàn)在不斷擴大的內(nèi)存和不斷增加的處理器數(shù)量,進一步降低暫停時間(pause time),同時兼顧良好的吞吐量。G1回收器和CMS比起來,有以下不同:

  1. G1垃圾回收器是compacting的,因此其回收得到的空間是連續(xù)的。這避免了CMS回收器因為不連續(xù)空間所造成的問題。如需要更大的堆空間,更多的floating garbage。連續(xù)空間意味著G1垃圾回收器可以不必采用空閑鏈表的內(nèi)存分配方式,而可以直接采用bump-the-pointer的方式;
  2. G1回收器的內(nèi)存與CMS回收器要求的內(nèi)存模型有極大的不同。G1將內(nèi)存劃分一個個固定大小的region,每個region可以是年輕代、老年代的一個。內(nèi)存的回收是以region作為基本單位的;

G1還有一個及其重要的特性:軟實時(soft real-time)。所謂的實時垃圾回收,是指在要求的時間內(nèi)完成垃圾回收。“軟實時”則是指,用戶可以指定垃圾回收時間的限時,G1會努力在這個時限內(nèi)完成垃圾回收,但是G1并不擔(dān)保每次都能在這個時限內(nèi)完成垃圾回收。通過設(shè)定一個合理的目標,可以讓達到90%以上的垃圾回收時間都在這個時限內(nèi)。

數(shù)據(jù)結(jié)構(gòu)

在了解G1垃圾回收器的算法前,先熟悉在G1中使用的一些數(shù)據(jù)結(jié)構(gòu)和概念是有用的。如果只是想大概了解一下G1垃圾回收器,那么此節(jié)可以跳過。即便想詳細了解G1回收器的細節(jié)也可以先跳過這一節(jié),直接閱讀下面的算法詳解,遇到了不明白的數(shù)據(jù)結(jié)構(gòu)和概念,再回來此處尋找解釋。

G1垃圾回收器的復(fù)雜難懂,有很大一部分原因是因為這些數(shù)據(jù)結(jié)構(gòu)。

Heap Region

本質(zhì)上來說,G1垃圾回收器依然是一個分代垃圾回收器。但是它與一般的回收器所不同的是,它引入了額外的概念,Region。G1垃圾回收器把堆劃分成一個個大小相同的Region。在HotSpot的實現(xiàn)中,整個堆被劃分成2048左右個Region。每個Region的大小在1-32MB之間,具體多大取決于堆的大小。

G1垃圾回收器的分代也是建立在這些Region的基礎(chǔ)上的。對于Region來說,它會有一個分代的類型,并且是唯一一個。即,每一個Region,它要么是young的,要么是old的。還有一類十分特殊的Humongous。所謂的Humongous,就是一個對象的大小超過了某一個閾值——HotSpot中是Region的1/2,那么它會被標記為Humongous。如果我們審視HotSpot的其余的垃圾回收器,可以發(fā)現(xiàn)這種對象以前被稱為大對象,會被直接分配老年代。而在G1回收器中,則是做了特殊的處理。
G1并不要求相同類型的region要相鄰。換言之,就是G1回收器不要求它們連續(xù)。當然在邏輯上,分代依舊是連續(xù)的。因此,一種典型的分配可能是:

G1 Regions

注:圖片來自G1: One Garbage Collector To Rule Them All

其中E代表的是Eden,S代表的是Survivor,H代表的是Humongous,剩余的深藍色代表的是Old(或者Tenured),灰色的代表的是空閑的region。
每一個分配的Region,都可以分成兩個部分,已分配的和未被分配的。它們之間的界限被稱為top。總體上來說,把一個對象分配到Region內(nèi),只需要簡單增加top的值。這個做法實際上就是bump-the-pointer。過程如下:

Region內(nèi)存分配

Region可以說是G1回收器一次回收的最小單元。即每一次回收都是回收N個Region。這個N是多少,主要受到G1回收的效率和用戶設(shè)置的軟實時目標有關(guān)。每一次的回收,G1會選擇可能回收最多垃圾的Region進行回收。與此同時,G1回收器會維護一個空間Region的鏈表。每次回收之后的Region都會被加入到這個鏈表中。
每一次都只有一個Region處于被分配的狀態(tài)中,被稱為current region。在多線程的情況下,這會帶來并發(fā)的問題。G1回收器采用和CMS一樣的TLABs的手段。即為每一個線程分配一個Buffer,線程分配內(nèi)存就在這個Buffer內(nèi)分配。但是當線程耗盡了自己的Buffer之后,需要申請新的Buffer。這個時候依然會帶來并發(fā)的問題。G1回收器采用的是CAS(Compate And Swap)操作。

為線程分配Buffer的過程大概是:

  1. 記錄top值;
  2. 準備分配;
  3. 比較記錄的top值和現(xiàn)在的top值,如果一樣,則執(zhí)行分配,并且更新top的值;否則,重復(fù)1;

顯然的,采用TLABs的技術(shù),就會帶來碎片。舉例來說,當一個線程在自己的Buffer里面分配的時候,雖然Buffer里面還有剩余的空間,但是卻因為分配的對象過大以至于這些空閑空間無法容納,此時線程只能去申請新的Buffer,而原來的Buffer中的空閑空間就被浪費了。Buffer的大小和線程數(shù)量都會影響這些碎片的多寡。

Remember Set和Card Table

RS(Remember Set)是一種抽象概念,用于記錄從非收集部分指向收集部分的指針的集合。
在傳統(tǒng)的分代垃圾回收算法里面,RS(Remember Set)被用來記錄分代之間的指針。在G1回收器里面,RS被用來記錄從其他Region指向一個Region的指針情況。因此,一個Region就會有一個RS。這種記錄可以帶來一個極大的好處:在回收一個Region的時候不需要執(zhí)行全堆掃描,只需要檢查它的RS就可以找到外部引用,而這些引用就是initial mark的根之一。

那么,如果一個線程修改了Region內(nèi)部的引用,就必須要去通知RS,更改其中的記錄。為了達到這種目的,G1回收器引入了一種新的結(jié)構(gòu),CT(Card Table)——卡表。每一個Region,又被分成了固定大小的若干張卡(Card)。每一張卡,都用一個Byte來記錄是否修改過。卡表即這些byte的集合。實際上,如果把RS理解成一個概念模型,那么CT就可以說是RS的一種實現(xiàn)方式。

從第一感覺,或者出于直覺的考慮,使用一個bit來記錄一張卡是否被修改過,就已經(jīng)足夠了。而使用一個byte會造成更多的空間開銷。但是實際上,使用一個byte來記錄一張卡是否被修改過,會比使用一個bit來記錄效率更高。更多細節(jié)參閱資料3。

在RS的修改上也會遇到并發(fā)的問題。因為一個Region可能有多個線程在并發(fā)修改,因此它們也會并發(fā)修改RS。為了避免這樣一種沖突,G1垃圾回收器進一步把RS劃分成了多個哈希表。每一個線程都在各自的哈希表里面修改。最終,從邏輯上來說,RS就是這些哈希表的集合。哈希表是實現(xiàn)RS的一種通常的方式之一。它有一個極大的好處就是能夠去除重復(fù)。這意味著,RS的大小將和修改的指針數(shù)量相當。而在不去重的情況下,RS的數(shù)量和寫操作的數(shù)量相當。

整個關(guān)系如下:

Remember Set

圖中RS的虛線表名的是,RS并不是一個和Card Table獨立的,不同的數(shù)據(jù)結(jié)構(gòu),而是指RS是一個概念模型。實際上,Card Table是RS的一種實現(xiàn)方式。

Remember Set的寫屏障

寫屏障是指,在改變特定內(nèi)存的值(實際上也就是寫入內(nèi)存)的時候額外執(zhí)行的一些動作。在大多數(shù)的垃圾回收算法中,都利用到了寫屏障。寫屏障通常用于在運行時探測并記錄回收相關(guān)指針(interesting pointer),在回收器只回收堆中部分區(qū)域的時候,任何來自該區(qū)域外的指針都需要被寫屏障捕獲,這些指針將會在垃圾回收的時候作為標記開始的根。JAVA使用的其余的分代的垃圾回收器,都有寫屏障。舉例來說,每一次將一個老年代對象的引用修改為指向年輕代對象,都會被寫屏障捕獲,并且記錄下來。因此在年輕代回收的時候,就可以避免掃描整個老年代來查找根。

G1垃圾回收器的寫屏障和RS是相輔相成的,也就是記錄Region內(nèi)部的指針。這種記錄發(fā)生在寫操作之后。對于一個寫屏障來說,過濾掉不必要的寫操作是十分有必要的。這種過濾既能加快賦值器的速度,也能減輕回收器的負擔(dān)。G1垃圾回收器采用的雙重過濾

  1. 過濾掉同一個Region內(nèi)部引用;
  2. 過濾掉空引用;

過濾掉這兩個部分之后,可以使RS的大小大大減小。

G1的垃圾回收器的寫屏障使用一種兩級的log buffer結(jié)構(gòu):

  1. global set of filled buffer:所有線程共享的一個全局的,存放填滿了的log buffer的集合;
  2. thread log buffer:每個線程自己的log buffer。所有的線程都會把寫屏障的記錄先放進去自己的log buffer中,裝滿了之后,就會把log buffer放到 global set of filled buffer中,而后再申請一個log buffer;

可以內(nèi)容可以參閱資料4的11.8節(jié)

Collect Set

Collect Set(CSet)是指,在Evacuation階段,由G1垃圾回收器選擇的待回收的Region集合。G1垃圾回收器的軟實時的特性就是通過CSet的選擇來實現(xiàn)的。對應(yīng)于算法的兩種模式fully-young generational mode和partially-young mode,CSet的選擇可以分成兩種:

  1. 在fully-young generational mode下:顧名思義,該模式下CSet將只包含young的Region。G1將調(diào)整young的Region的數(shù)量來匹配軟實時的目標;
  2. 在partially-young mode下:該模式會選擇所有的young region,并且選擇一部分的old region。old region的選擇將依據(jù)在Marking cycle phase中對存活對象的計數(shù)。G1選擇存活對象最少的Region進行回收。

SATB(snapshot-at-the-beginning)

SATB(snapshot-at-the-beginning),是最開始用于實時垃圾回收器的一種技術(shù)。G1垃圾回收器使用該技術(shù)在標記階段記錄一個存活對象的快照("logically takes a snapshot of the set of live objects in the heap at the start of marking cycle")。然而在并發(fā)標記階段,應(yīng)用可能修改了原本的引用,比如刪除了一個原本的引用。這就會導(dǎo)致并發(fā)標記結(jié)束之后的存活對象的快照和SATB不一致。G1是通過在并發(fā)標記階段引入一個寫屏障來解決這個問題的:每當存在引用更新的情況,G1會將修改之前的值寫入一個log buffer(這個記錄會過濾掉原本是空引用的情況),在最終標記(final marking phase)階段掃描SATB,修正SATB的誤差。

SATB的log buffer如RS的寫屏障使用的log buffer一樣,都是兩級結(jié)構(gòu),作用機制也是一樣的。

細節(jié)可以參閱資料2,6

Marking bitmaps和TAMS

Marking bitmap是一種數(shù)據(jù)結(jié)構(gòu),其中的每一個bit代表的是一個可用于分配給對象的起始地址。舉例來說:

bitmap

其中addrN代表的是一個對象的起始地址。綠色的塊代表的是在該起始地址處的對象是存活對象,而其余白色的塊則代表了垃圾對象。
G1使用了兩個bitmap,一個叫做previous bitmap,另外一個叫做next bitmap。previous bitmap記錄的是上一次的標記階段完成之后的構(gòu)造的bitmap;next bitmap則是當前正在標記階段正在構(gòu)造的bitmap。在當前標記階段結(jié)束之后,當前標記的next bitmap就變成了下一次標記階段的previous bitmap。
TAMS(top at mark start)變量,是一對用于區(qū)分在標記階段新分配對象的變量,分別被稱為previous TAMS和next TAMS。在previous TAMS和next TAMS之間的對象則是本次標記階段時候新分配的對象。如圖:

previous TMAS 和 next TAMS

白色region代表的是空閑空間,綠色region代表是存活對象,橙色region代表的在此次標記階段新分配的對象。注意的是,在橙色區(qū)域的對象,并不能確保它們都事實上是存活的。

算法詳解

整個算法可以分成兩大部分:

  1. Marking cycle phase:標記階段,該階段是不斷循環(huán)進行的;
  2. Evacuation phase:該階段是負責(zé)把一部分region的活對象拷貝到空Region里面去,然后回收原本的Region空間,該階段是STW(stop-the-world)的;

而算法也可以分成兩種模式:

  1. fully-young generational mode:有時候也會被稱為young GC,該模式只會回收young region,算法是通過調(diào)整young region的數(shù)量來達到軟實時目標的;
  2. partially-young mode:也被稱為Mixed GC,該階段會回收young region和old region,算法通過調(diào)整old region的數(shù)量來達到軟實時目標;

有趣的地方是不論處在何種模式之下,yong region都在被回收的范圍內(nèi)。而old region只能期望于Mixed GC。但是,如同在CMS垃圾回收器中遇到的困境一樣,Mixed GC可能來不及回收old region。也就說,在需要分配老年代的對象的時候,并沒有足夠的空間。這個時候就只能觸發(fā)一次full GC。

算法會自動在young GC和mixed GC之間切換,并且定期觸發(fā)Marking cycle phase。HotSpot的G1實現(xiàn)允許指定一個參數(shù)InitiatingHeapOccupancyPercent,在達到該參數(shù)的情況下,就會執(zhí)行marking cycle phase。

算法并不使用在對象頭增加字段來標記該對象,而是采用bitmap的方式來記錄一個對象被標記的情況。這種記錄方法的好處就是在使用這些標記信息的時候,僅僅需要掃描bitmap而已。G1統(tǒng)計一個region的存活的對象,就是依賴于bitmap的標記。

Marking Cycle Phase

算法的Marking cycle phase大概可以分成五個階段:

  1. Initial marking phase:G1收集器掃描所有的根。該過程是和young GC的暫停過程一起的;
  2. Root region scanning phase:掃描Survivor Regions中指向老年代的被initial mark phase標記的引用及引用的對象,這一個過程是并發(fā)進行的。但是該過程要在下一個young GC開始之前結(jié)束;
  3. Concurrent marking phase:并發(fā)標記階段,標記整個堆的存活對象。該過程可以被young GC所打斷。并發(fā)階段產(chǎn)生的新的引用(或者引用的更新)會被SATB的write barrier記錄下來;
  4. Remark phase:也叫final marking phase。該階段只需要掃描SATB(Snapshot At The Beginning)的buffer,處理在并發(fā)階段產(chǎn)生的新的存活對象的引用。作為對比,CMS的remark需要掃描整個mod union table的標記為dirty的entry以及全部根;
  5. Cleanup phase:清理階段。該階段會計算每一個region里面存活的對象,并把完全沒有存活對象的Region直接放到空閑列表中。在該階段還會重置Remember Set。該階段在計算Region中存活對象的時候,是STW(Stop-the-world)的,而在重置Remember Set的時候,卻是可以并行的;

Initial marking phase

該階段掃描所有的根,與CMS類似。所不同的是,該階段是和young GC一起的。這里的young GC實際上是指的就是fully-young generational mode。

Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Guide的原文是"This phase is piggybacked on a normal (STW) young garbage collection"。

Root region scanning phase

該過程主要是掃描Survivor region中指向老年代的,在initial mark phase標記的引用及其引用的對象。這是一個很奇怪的步驟,因為在前面不論是Parallel Collector還是CMS,都沒有這么一個步驟。

要理解這一點,要注意的是,算法的兩種模式,不論是young GC還是mixed GC,都需要回收young region。因為實際上RS是不記錄從young region出發(fā)的指針,例如,這部分指針包括young region - young region,也包括young-region - old region指針。那么就可能出現(xiàn)一種情況,一個老年代的存活對象,只被年輕代的對象引用。在一次young GC中,這些存活的年輕代的對象會被復(fù)制到Survivor Region,因此需要掃描這些Survivor region來查找這些指向老年代的對象的引用,作為并發(fā)標記階段掃描老年代的根的一部分。

在理解了這一點的基礎(chǔ)上,那么對于階段必須在下一次young GC啟動前完成的要求,也就理解了。因為如果第二次的young GC啟動了,那么這個過程中,survivor region就可能發(fā)生變化。這個時候執(zhí)行root region phase就會產(chǎn)生錯誤的結(jié)果。

Concurrent marking phase

在標記階段,會使用到一個marking stack的東西。G1不斷從marking stack中取出引用,遞歸掃描整個堆里的對象圖,并且在bitmap上進行標記。這個遞歸過程采用的是深度遍歷,會不斷把對象的域入棧。

在并發(fā)標記階段,因為應(yīng)用還在運行,所以可能會有引用變更,包括現(xiàn)有引用指向別的對象,或者刪除了一個引用,或者創(chuàng)建了一個新的對象等。G1采用的是使用SATB的并發(fā)標記算法。

在資料6中記錄了使用SATB的兩條原則:

  1. All accessible cells at the beginning of the garbage collection are eventually marked during the marked phase;
  2. Newly alocated cells during the garbage collection are never collected during the sweep phase of that garbage collection

在G1中,該算法的關(guān)鍵在于,如果在并發(fā)標記的時候,出現(xiàn)了引用修改(不包含新分配內(nèi)存給對象),那么寫屏障會把這些引用的原始值捕獲下來,記錄在log buffer中。而后再處理。后續(xù)的所有的標記,都是從原來的值出發(fā),而不是從新的值出發(fā)的。

SATB是一個邏輯上存在概念,在實際中并沒有任何真的實際的數(shù)據(jù)結(jié)構(gòu)與之對應(yīng)。叫這個名字,是因為,一旦進入了concurrent marking階段,那么該在該階段的運行過程中,即便應(yīng)用修改了引用,但是因為SATB的寫屏障記錄下來了原始的值,在遍歷整個堆查找存活對象的時候,使用的依然是原來的值。這就是在邏輯上保持了一個snapshot at the beginning of concurrent marking phase。

在處理新創(chuàng)建的對象,G1采用了不同的方式。G1用了兩個TAMS變量了判斷新創(chuàng)建的對象。一個叫做previous TAMS,一個叫做next TAMS。位于兩者之間的對象就是新分配的對象。

并發(fā)標記階段,bitmap和TAMS的作用如圖:

bitmap和TAMS的作用

注:圖片引自資料2

該圖的詳細解釋如下:

  1. A是第一次marking cycle的initial marking階段。next bitmap尚未標記任何存活對象,而此時的previous TAMS被初始化為region內(nèi)存地址起始值,next TAMS被初始化為top。top實際上就是一個region未分配區(qū)域和已分配區(qū)域的分界點;
  2. B是經(jīng)過concurrent marking階段之后,進入了remark階段。此時存活對象的掃描已經(jīng)完成了,因此next bitmap構(gòu)造好了,剛好代表的是當下狀態(tài)中region中的內(nèi)存使用情況。注意的是,此時top已經(jīng)不再與next TAMS重合了,top和next TAMS之間的就是在前面標記階段之時,新分配的對象;
  3. C代表的是clean up階段。C和B比起來,next bitmap變成了previous bitmap,而在bitmap中標記為垃圾(也就是白色區(qū)域的)的對應(yīng)的region的區(qū)域也被染成了淺灰色。這并不是指垃圾對象已經(jīng)被清掃了,僅僅是標記出來了。同時next TAMS和previous TAMS也交換了角色;
  4. D代表的是下一個marking cycle的initial marking階段,該階段和A類似,next TAMS重新被初始化為top的值;
  5. EF就是BC的重復(fù);

Remark phase

該階段是一個STW的階段。引入該階段的目的,是為了能夠達到結(jié)束標記的目標。要結(jié)束標記的過程,要滿足三個條件:

  1. concurrent marking已經(jīng)追蹤了所有的存活對象;
  2. marking stack是空的;
  3. 所有的log都被處理了;

前兩個條件是很容易達到的,但是最后一個是很困難的。如果不引入一個STW的remark過程,那么應(yīng)用會不斷的更新引用,也就是說,會不斷的產(chǎn)生log,因而永遠也無法達成完成標記的條件。

Clean up

該階段主要完成:

  1. 統(tǒng)計存活對象,這是利用RS和bitmap來完成的,統(tǒng)計的結(jié)果將會用來排序region,以用于下一次的CSet的選擇;
  2. 重置RSet;
  3. 把空閑region放到空閑region列表中;

該階段比較容易引起誤解地方在于,Clean up并不會清理垃圾對象,也不會執(zhí)行存活對象的拷貝。也就是說,在極端情況下,該階段結(jié)束之后,空閑Region列表將毫無變化,JVM的內(nèi)存使用情況也毫無變化。

Evacuation

Evacuation階段STW的,大概可以分成兩個步驟:第一個步驟是從Region中選出若干個Region進行回收,這些被選中的Region稱為Collect Set(簡稱CSet);而第二個步驟則是把這些Region中存活的對象復(fù)制到空閑的Region中去,同時把這些已經(jīng)被回收的Region放到空閑Region列表中。
這兩個步驟又可以被分解成三個任務(wù):

  1. 根據(jù)RS的日志更新RS:只有在處理完了RS的日志之后,RS才能夠保證是準確的,完整的,這也是Evacuation是STW的重要原因;
  2. 掃描RS和其余的根來確定存活對象:該階段實際上最主要依賴于RS;
  3. 拷貝存活對象:該階段只要從2中確定的根觸發(fā),沿著引用鏈一直追溯下去,將存活對象復(fù)制到新的region就可以。這個過程中,可能有一部分的年輕代對象會被提升到老年代;

Evacuation的時機

Evacuation的觸發(fā)時機在不同的模式下會有一些不同。在不同的模式下都相同的是,只要堆的使用率達到了某個閾值,就必然會觸發(fā)Evacuation。這是為了確保在Evacuation的時候有足夠的空閑Region來容納存活對象。

在young GC的情況下,G1會選擇N個region作為CSet,該CSet首先需要滿足軟實時的要求,而一旦已經(jīng)有N個region已經(jīng)被分配了,那么就會執(zhí)行一次Evacuation。

G1會盡可能的執(zhí)行mixed GC。唯一的限制就是mix GC也需要滿足軟實時的要求。

G1觸發(fā)Evacuation的原則大概是:

  1. 如果被分配的young region數(shù)量滿足young GC的要求,那么就會觸發(fā)young GC;
  2. 如果被分配的young region數(shù)量不滿足young GC,就會進一步考察加上old region的數(shù)量,能否滿足old GC的要求;

為了理解這一點,可以舉例來說,假如回收一個old region的時間是回收一個young region的兩倍,也就是young region花費時間T,old region花費2T,在滿足軟實時目標的情況下,GC只能回收8T的region,那么:

  1. 假如應(yīng)用現(xiàn)在只分配k(k<8)塊young region,沒有分配任何old region。這個時候又分配了一個old region,那么這個時候會立刻觸發(fā)一次mixed GC,此次GC會選擇k塊young region和一塊old region;
  2. 因此,在這種假設(shè)下,只要有可以回收的old region的時候,總是會先回收old region;
  3. 在沒有任何old region的情況下,才有可能觸發(fā)young region。

當然,在一般情況下,這些假設(shè)是不成立的。讀者可以思考一下,在young GC和mixed GC達到軟實時的要求下,young region和old region之間回收的花銷不同會導(dǎo)致young GC和mixed GC會在什么情況下觸發(fā)。

資料

  1. Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide
  2. David Detlefs, Christine Flood, Steve Heller, Tony Printezis. Garbage-First Garbage Collection
  3. Urs Ho?lzle. A Fast Write Barrier for Generational Collectors
  4. 垃圾回收算法手冊——自動內(nèi)存管理的藝術(shù)
  5. 請教G1算法的原理——RednaxelaFX的回答
  6. Taichi Yuasa. Real-time garbage collection on general-purpose machines.
  7. Christine H. Flood, David Detlefs, Nir Shavit, and Xiaolan Zhang. Parallel garbage collection for shared memory multiprocessors
  8. 名詞連接貼-RednaxelaFX對Remeber Set和Card Table的解釋
  9. G1: One Garbage Collector To Rule Them All
  10. Poonam Parhar. Understanding G1 GC Logs
  11. Getting Started with the G1 Garbage Collector
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內(nèi)容