1. 前言java
1.1 概念:清理內存中不會再被使用的對象程序員
1.2 背景:若是內存中的垃圾不被清理,會致使內存溢出算法
1.3 回收區域:方法區和Java堆。【JVM的內存區域主要包括5個區域:Java棧(虛擬機棧)、本地方法棧、PC寄存器、方法區、Java堆。由於Java棧、本地方法棧、以及PC寄存器是線程專有的,當方法結束或者線程結束時,內存就天然就跟着回收了。而Java堆和方法區的內存分配和回收是動態的,正是垃圾回收器因此須要關注的部分。】性能優化
1.4 經常使用的垃圾回收算法:引用計數法(Reference Counting)、標記清除法(Mark-Sweep)、複製算法(Copying)、標記壓縮法(Mark-Compact)、分代算法(Generational Collecting)及分區算法(Region)性能
2. 算法演進優化
2.1 引用計數法【Java垃圾回收未採用】spa
2.1.1 思想:對於對象A,若是被引用,A的引用計數器就+1;當引用失效時,A的引用計數器-1.當對象A的引用計數器的值爲0,則對象A被回收。線程
2.1.2 缺陷:對象
2.1.2.1 沒法處理循環引用的狀況。【例如:對象A和對象B相互引用,卻沒有被其餘對象引用。此時,A和B應該被回收。然而,對象A和B的引用計數器卻不爲0,沒法被回收。】 blog
2.1.2.2 每次對象被引用/釋放引用的時候,都會伴隨着運算操做,對系統性能會有必定的影響。
由於循環引用的對象沒法被回收,爲了解決該缺陷,因而產生了可達對象的概念。根據可達對象的概念,產生了標記清除法。
2.2 標記清除法【現代垃圾回收算法的基礎】
2.2.1 基礎:
2.2.1.0 在Java語言中,可做爲根節點的對象包括如下幾種:
a. Java棧中引用的對象
b. 方法區中靜態屬性以及常量引用的對象;
c. 本地方法棧中JNI(Native方法)引用的對象。
2.2.1.1 可達對象:經過根對象進行引用搜索,最終能夠達到的對象。
2.2.1.2 不可達對象:經過根對象進行引用搜索,最終沒有被引用到的對象。
2.2.2 思想:經過標記、清除兩個階段實現垃圾回收
2.2.2.1 標記階段:根據根節點,標記全部的可達對象【根據根節點標記可達對象後,還會進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法。在finalize()中沒有從新與引用創建關聯的對象纔會被真正回收。】
2.2.2.2 清除階段:清除全部的不可達對象
2.2.3 缺陷:
2.2.3.1 產生大量的空間碎片,工做效率低
標記清除法是對一塊連續的空間進行回收,回收後的空間是不連續的【如圖】。在對象的堆空間分配過程當中,尤爲是大對象的內存分配,不連續內存空間的工做作效率要低於連續空間。
由於不連續內存空間的工做效率低,爲了解決這個問題,因而想到:新開闢一個工做空間,將可達對象複製到新的內存空間,保證空間使用的連續性。
2.3 複製算法【新生代算法】
2.3.1 思想:將原有的內存空間分爲兩塊,每次使用其中一塊。垃圾回收時,將正在使用的內存中的可達對象複製到未使用的內存塊中,清除正在使用的內存塊中的全部對象,交換兩個內存的角色,完成回收。
2.3.2 缺陷:
2.3.2.1 將系統內存摺半,佔用的系統內存太大。
複製算法的高效性是創建在存活對象少,垃圾對象對象多的前提下。對於存活對象多的狀況,複製成本高,須要其它算法來替代。
2.4 標記壓縮法【老年代算法】
2.4.1 原理:標記清除算法執行完成後,再進行一次內存碎片整理。
2.4.2 思想:從根節點出發,將全部的可達對象進行標記,而後將標記對象壓縮到內存的一端。最後,清除邊界以外的全部空間。
在上述算法中,它們都有各自獨特的優點,並無一種算法能夠徹底替代其它算法。所以,正確的方式是:根據垃圾回收對象的特性,使用合適的算法回收。基於這種狀況,產生了分代的思想。
2.5 分代算法
2.5.1 思想:根據對象的特色,將內存區域分紅幾塊;根據每塊內存區域的特色,使用不一樣的回收算法。
2.5.2 應用:新生代使用複製算法,老年代使用標記壓縮法。
既然能夠根據對象的生命週期(時間角度)進行回收,那麼根據對象的空間分佈(空間角度)也能夠進行回收。
2.6 分區算法
2.6.1 背景:在相同條件下,堆空間越大,一次GC所用的時間越長,從而產生的停頓也越長。
2.6.2 思想:將堆空間劃分紅連續的小區域,每一個小區域獨立使用、獨立回收。
3. 補充
3.1 新生代串行垃圾回收器中,使用複製算法的思想。
3.1.1 基礎:新生代分爲eden區、from區、to區。其中,from區和to區也被稱爲survivor區,能夠視爲用於複製的大小相等、地位相等、且能夠角色互換的兩個空間塊。
3.1.2 思路:垃圾回收時,Eden區和正在使用的survivor區(假設是from區)中的可達對象會被複制到未被使用的survivor區(to區),而後清空Eden區和from區。
3.1.3 備註:from區中的大對象或者老年對象會直接進入老年區;若是to區空間不足,對象也會進入老年區
3.2 複製算法的高效性是創建在可達對象少,不可達對象多的前提下,所以複製算法只用於新生代【新生代,垃圾對象一般會多於存活對象;老年代大部分對象都是存活對象】。老年代採用標記壓縮法
3.3 新生代GC與老年代GC對比:
3.4 Java中的引用
判斷對象是否存活都與「引用」有關。在Java中,引用分爲四種:強引用、軟引用、弱引用、虛引用。
3.4.1 強引用
是代碼中普片存在的一種引用,形如:Object obj = new Object(); 只要強引用還存在,垃圾回收器就永遠不會回收被引用的對象。
3.4.2 軟引用
用來描述一些還有用可是非必須的對象。對於軟引用關聯着的對象,在系統將要發生內存溢出異常(即:內存不足)時,纔會被回收。
3.4.3 弱引用
用來描述非必須對象,它的強度比軟引用更弱。當GC發生時,必定會被回收(不管內存是否充足)。
3.4.4 虛引用
也叫幽靈引用或幻影引用,是最弱的一種引用。經過虛引用沒法建立對象實例。它的做用是可以在這個對象被回收時收到一個系統通知。
3.5 方法區如何判斷是否須要回收
方法區主要回收的內容爲:廢棄的常量和無用的類。對於廢棄的常量能夠經過引用的可達性來判斷,可是對於無用的類則須要經過如下3個條件判斷:
3.5.1 該類的全部實例都已經被回收
3.5.2 該類的CLassLoader已經被回收
3.5.3 該類對象的java.lang.Class對象沒有在任何地方被使用(即:該類沒有被反射訪問)
4. 參考文獻:
4.1 葛一鳴:《實戰Java虛擬機 – JVM故障診斷與性能優化》
4.2 程序員內參 : 《JVM的垃圾回收機制總結》