技.藝.道:認識GC垃圾回收機制

一、什麼是垃圾

垃圾就是無用的東西。那怎麼區分一個東西有用無用呢?

1.垃圾的判定

1.1 引用計數法

原理:

最初人們的想法是這樣:一個東西,我拿來用一次就記作+1,用好了放回去就記作-1,如果一個東西最後在盤算的時候,發現它的得分是0,那麼就可以認爲它目前沒有被誰拿去用,那我現在把它丟掉不會影響到任何人現在正在做的事情。想法是不是很好?

問題:

可是,如果有一天,我們有一個螺絲釘,一個螺絲帽。我們取出螺絲釘和螺絲帽,並把螺絲帽和螺絲釘擰在一起,擰成一個螺組。雖然這個螺組我們沒有在用了,可是由於螺絲釘在和螺絲帽配對,螺絲帽也在忙着和螺絲釘配對,因此他倆都不能被放回去。使得這兩個工具無意義的佔用着空間,因爲他們的組合體現在實際上並沒有什麼用。

使用場景:

據說Lisp,Python,Ruby等的垃圾收集算法採用的就是引用計數算法。

1.2 可達性分析算法

原理:找不在葡萄串上的葡萄,只需要捏着一串葡萄的根部提起來,落在盤子裏的葡萄就是不在葡萄串上的葡萄了。這就是可達性分析算法的原理。即:先找到所謂的根節點,從跟出發,所有用到的都不能回收,因爲都有用,其他的用不着的都可以回收。

問題:

暫未明確

使用場景:

JVM中使用的通常都是可達性分析算法來判斷哪些是垃圾對象。

細說:

jvm掃描堆中的對象,看是否能夠沿着GCRoot對象爲起點的引用鏈找到該對象,找不到,表示可以回收,反之則保留。在實際的操作中通常是

可以通過jmap 工具抓取程序當今的堆內存使用快照
jmap -dump:format=b,live,file=1.bin pid
表示通過jmap 抓取堆內存快照,格式是2進制格式(format=b);
live:表示只抓取存活的對象而忽略掉已經被回收的對象。
file=1.bin :表示抓取的文件會存放在1.bin這個文件中。
通過Eclipse Memory Analyzer工具打開抓取的文件,在java basics->GC Roots中查看可以作爲root的 對象。
1.System Class 系統核心類
2.Native Stack 原生類
3.Thread 線程相關:活動線程。線程的每次方法調用產生的棧幀都可以作爲方法對象。
4.Busy Monitor 正在加鎖的對象

“可達性分析算法”就是看堆中的每個對象是不是在根節點的引用鏈上。如果在根節點上,就不能被回收,否則可以。

“引用鏈”即通過引用關係產生的鏈狀結構。

關於“引用”:

引用可不單是 A a = new A();

這個叫做“強引用”。其他引用還有軟引用、弱引用、虛引用、終接器引用。

【後面我單獨寫一篇來講引用。】

這裏只做簡單的說明。

  • 被GC Roots對象強引用的對象不會被回收;
  • 被GC Roots對象軟引用的對象在發生了GC和GC後內存仍然不足時它會被垃圾回收;
  • 被GC Roots對象弱引用的對象在發生了GC時它會被垃圾回收;

應用:當內存緊張時,可以使用軟鏈接。

 

二、垃圾回收發生在哪裏

開門見山,這裏講jvm內存結構

堆內存中,分爲新生代,老年代和StringTable(串表區)。其中新生代分爲伊甸園區、倖存區From、倖存區To。

通常情況下,對象在伊甸園誕生,隨着一輪小規模垃圾回收,一些有用的對象就會被複制到“倖存區To”,這些被複制過來的對象的年齡會加一,而伊甸園區則會整個被回收,當這個過程結束之後,倖存區To就會與倖存區From交換名字,即:此時倖存區To變爲了空的,對象都到了倖存區From。等到下次小規模回收的時候,倖存區From和伊甸園裏的有用對象年齡會再統一加一,年齡達到老年標準的對象可以直接進入老年代,沒有達到的會被放入倖存區To,然後倖存區To和倖存區From再交換名稱。

意外情況:

天神下凡:當新生區連續內存空間不足以存放一個大對象而老年代存得下時,對象會在老年代被創建。

補充說明:

StringTable:串池,當創建一個字符串變量時,會先從串池中搜索是否存在該字符串,若有,則直接引用,若沒有,則創建該字符串並將其添加到串池,並引用。它的本質是一個HashTable,大小是固定的不可擴容。

三、垃圾回收的種類

上面提到的“小規模垃圾回收”實際上叫做“minor GC”。

1.Minor GC

  • 負責從新生代進行垃圾回收。
  • 新對象總是在伊甸園產生的(當新生代連續空間不足時,大對象可以在老年代被創建)
  • 當新生代內存不足時,觸發minorGC,將伊甸園中的有用對象複製到倖存區To年齡+1,並將倖存區To與倖存區From互換名稱,接着將整個伊甸園清理掉。
  • 當第二次minorGC時,會將伊甸園和倖存區From中的有用元素都放入倖存區To,對象年齡+1,然後清空伊甸園和倖存區From,再將To和From互換名稱。
  • 隨着一次次的minorGC,當對象的年齡到15歲時(最大),會被移動到老年代中。對象的壽命最大爲15,因爲對象頭用來存放年齡的位置爲4bit。當新生代空間不足時會將一些對象放入老年代。
  • minor GC發生時,會引發stop the world,暫停用戶線程,等GC結束之後用戶線程纔會恢復運行。
  • 當老年代空間不足,會先嚐試觸發minorGC,如果之後空間仍然不足,會觸發一次full GC。它同樣會引起一次 stop the world,而且stw時間更長。

2.Full GC

  • 在老年代空間不足時負責從老年代進行垃圾回收。

3.小結

  • 新生代內存不足發生的垃圾收集都是 minor GC
  • Serial GC和Parallel GC在老年代內存不足時發生的垃圾收集都是 Full GC
  • 在CMS和G1中,當老年代內存不足時,當老年代內存佔堆內存45%,進入併發標記階段,進而進入混合收集階段。在這兩個階段中,若回收速度大於垃圾產生速度,則不會產生Full GC;若回收速度小於垃圾產生速度,即垃圾收集不過來,則會退化爲並行收集,進而發生Full GC。

三、如何清理

1.標記清除算法

清理垃圾的方式有很多種,比如自己準備一些便利貼,哪些東西沒用了你就給它貼一張寫着
“垃圾”的便利貼,等到週末有空了,把所有貼了“垃圾”便利貼的東西都給扔到垃圾箱去就可以了(記得自己在扔的時候分個類啊!不然可能會罰款)這就是標記清除算法。

2.標記整理算法

當然你也可以在標記之後,把這些垃圾儘可能的放在一塊,這樣的好處就是你的空間會集中一些,不至於這裏一點空,那裏一點空的。這就是標記整理算法。

3.複製算法

當你家足夠大,或者是你的垃圾足夠多的,錢也足夠多,那你就不用像上面那麼做了,可以直接把有用的東西再買一份放到另一間空屋子就可以了。這就是複製算法。

四、有哪些垃圾回收器

按原理分

1.串行 垃圾回收器

特點:

  • 單線程
  • 適用於堆內存較小,適合個人電腦

Serial:新生代的垃圾回收器,使用“複製”算法回收垃圾。

SerialOld:老年代的垃圾回收器,使用“標記整理”算法回收垃圾。

 

2.吞吐量優先 垃圾回收器

特點:

  • 多線程
  • 堆內存較大,多核cpu
  • 讓單位時間內,STW的時間最短 如:0.2+0.2=0.4(STW:stop the world,即當執行到垃圾回收的某個階段時,會將所有用戶線程暫停下來,這段暫停時間被稱爲SWT)

開啓開關:-XX:+UseParallelGC~-XX:+UseParallelOldGC~(在jdk1.8開始,此開關默認開啓。即默認使用吞吐量優先垃圾回收器)

工作模式:-XX:+UseAdaptiveSizePolicy:自適應調整新生代的大小

GC時間佔比:-XX:GCTimeRatio = ratio:用來設定GC時間與程序運行時間的比值。1/(1+ratio);默認是99,通常需要配置成19.

最大暫停毫秒數:-XX:MaxGCPauseMillis=ms:默認值是200;

3.響應時間優先 垃圾回收器

 

並行標記清除 垃圾回收器

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~SerialOld

-XX:+UseConcMarkSweepGC :用來進行老年代垃圾回收;當其併發失敗,切換至SerialOld進行垃圾回收

-XX:+UseParNewGC:工作在新生代的垃圾回收器

並行度與併發數:

-XX:ParallelGCThreads=n~-XX:ConcGCThreads=threads

並行的垃圾回收線程數:通常等於cpu核數。

併發的垃圾回收線程數:通常設置爲並行數的1/4。即:留着剩餘3/4的線程數用來執行用戶邏輯。

並行數是垃圾回收線程能使用的最多線程數,併發數是可以同時運行的垃圾回收器線程數。

執行cms內存回收時的內存佔比:-XX:CMSInitiatingOccupancyFration=percent

重新標記前對新生代進行垃圾回收:-XX:+CMSScavengeBeforeRemark

cms存在由碎片過多導致的同步失敗,會使垃圾收集時間劇增。這是其一大問題。

按實現分

CMS垃圾回收器

CMS(Concurrent Mark Sweep),是一款併發的、使用標記-清除算法的垃圾回收器。

具體說明可以看這篇https://www.jianshu.com/p/2a1b2f17d3e4

G1垃圾回收器

  • 同時注重吞吐量和低延遲,默認的暫停目標是200ms
  • 支持超大堆空間,會將堆劃分爲多個大小相等的Region
  • 整體上是標記+整理算法,兩個區域之間是複製算法
  • 這是一種在jdk9中默認使用的垃圾回收器。下面有一篇詳細說明。

https://www.jianshu.com/p/aef0f4765098

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章