必知必會JVM垃圾回收——對象搜索算法與回收算法

垃圾回收(GC)是JVM的一大殺器,它使程序員能夠更高效地專一於程序的開發設計,而不用過多地考慮對象的建立銷燬等操做。可是這並非說程序員不須要了解GC。GC只是Java編程中一項自動化工具,任何一個工具都有它適用的範圍,當超出它的範圍的時候,可能它將不是那麼自動,而是須要人工去了解與適應地適用。程序員

擁有必定工做年限的程序員,在工做期間確定會常常碰到像內存溢出、內存泄露、高併發的場景。這時候在應對這些問題或場景時,若是對GC不瞭解,極可能會成爲我的的發展瓶頸。算法

接下來的兩文將詳細學習下JVM中垃圾回收(GC)的各個知識要點。本文先從GC的算法開始先了解,鋪墊好基礎,下一篇再詳細講JVM具體的GC實現。編程

GC對象搜索算法

垃圾回收,第一件事就是要搞清楚哪些東西是垃圾,然後才能對這些垃圾進行回收。微信

那麼有什麼辦法識別對象是否爲無用的垃圾呢?狹義地,怎麼判斷對象是否沒被引用呢?併發

一般有如下兩種算法去識別判斷編程語言

  • 引用計數算法

這個算法很是簡單。給對象一個計數器,每當這個對象被引用了,計數器值加一;引用失效,則減一。但這個對象計數值爲0的時候,證實是無用對象,能夠被GC程序回收掉。這種算法比較普遍應用在一些腳本語言上,如FLASH、PYTHON等。
可是引用計數算法沒法解決對象間相互引用的問題。當a對象引用了b對象,b對象也引用了a對象,這樣a、b兩個對象的計數器值都不會爲0,即便這兩個對象都被其餘對象所引用,最終致使這些對象一直沒法被回收。這種狀況每每會出如今比較複雜的編程語言中。
高併發

  • 可達性分析算法

可達性分析算法(GC roots算法),普遍應用於主流的商用語言。設置一個根節點,從圖論角度來看,只要從該節點可達一個對象,證實這個對象是存活的(被引用)。
工具

一般地,GC會包含如下區域的對象:性能

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象;
  • 方法區中類靜態屬性引用的對象;
  • 方法區中常量引用的對象;
  • 本地方法棧中JNI(即通常說的Native方法)引用的對象;

垃圾回收算法

瞭解完垃圾是怎麼找出來後,接下來看看它們是怎麼被清除的。如下介紹幾種清除的算法。學習

標記-清除算法(Mark-Sweep)

標記-清除,顧名思義,先標記垃圾,再清除。它是GC最基礎的算法,後續不少算法都是基於它上面去改進的。
標記的過程在上面搜索GC對象已經介紹過了。被標記的對象,在統一GC的時候會把標記的對象清除掉。這個算法比較簡單,不作過多贅述。


這個算法有一個很明顯的缺點,就是在垃圾回收後會產生大量不連續的碎片空間,致使程序要申請較大的對象時常沒法找到合適的內存空間,迫使再次GC。

複製算法

複製算法的存在,正是爲了解決內存碎片問題。而且這個算法也是分代算法的基礎。

將內存分爲大小相等的兩塊,每次程序只使用其中一塊,當GC發生的時候,把存活的對象複製到另一塊內存中,整齊的排列,而後清空原來的那塊內存。

能夠看到,這種算法有點新生代轉移到老年代的感受。

缺點:

  1. 把內存可以使用的空間減小了一半,形成空間的浪費。
  2. 對象存活數量較多的時候,複製性能比較差

這種缺點,在老年代中,對象存活率比較高的場景下是很是場景間。

標記-整理算法(Mark-Compact)

針對複製算法的兩個缺點,在老年代通常會用這種標記-整理算法。

把存活的對象移到內存的一段,而後把剩餘的空間所有清空掉。

分代收集算法

分代算法並非一個特定的算法,也沒有什麼新的內容。而是把內存分紅多個區域,通常爲新生代、老年代等。而後根據不一樣區域不一樣的特色,用不一樣回收算法去回收垃圾。

例如新生代,對象存活率低,比較適用複製算法。老年代存活率高,比較適用Mark-Compact算法。

目前幾乎全部的商業虛擬機都是採用分代收集的。具體不一樣的收集器在下一文再詳細說明。


更多技術文章、精彩乾貨,請關注
博客:zackku.com
微信公衆號:Zack說碼

相關文章
相關標籤/搜索