深刻理解java虛擬機【垃圾回收算法】

Java虛擬機的內存區域中,程序計數器、虛擬機棧和本地方法棧三個區域是線程私有的,隨線程生而生,隨線程滅而滅;棧中的棧幀隨着方法的進入和退出而進行入棧和出棧操做,每一個棧幀中分配多少內存基本上是在類結構肯定下來時就已知的,所以這三個區域的內存分配和回收都具備肯定性。垃圾回收重點關注的是堆和方法區部分的內存。java

經常使用的垃圾回收算法有:web

(1).引用計數算法:算法

給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器都爲0的對象就是再也不被使用的,垃圾收集器將回收該對象使用的內存。spa

引用計數算法實現簡單,效率很高,微軟的COM技術、ActionScript、Python等都使用了引用計數算法進行內存管理,可是引用計數算法對於對象之間相互循環引用問題難以解決,所以java並無使用引用計數算法。線程

(2).根搜索算法:指針

經過一系列的名爲「GC Root」的對象做爲起點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可以使用的,垃圾收集器將回收其所佔的內存。orm

主流的商用程序語言C#、java和Lisp都使用根搜素算法進行內存管理。對象

在java語言中,可做爲GC Root的對象包括如下幾種對象:生命週期

a. java虛擬機棧(棧幀中的本地變量表)中的引用的對象。ip

b.方法區中的類靜態屬性引用的對象。

c.方法區中的常量引用的對象。

d.本地方法棧中JNI本地方法的引用對象。

java方法區在Sun HotSpot虛擬機中被稱爲永久代,不少人認爲該部分的內存是不用回收的,java虛擬機規範也沒有對該部份內存的垃圾收集作規定,可是方法區中的廢棄常量和無用的類仍是須要回收以保證永久代不會發生內存溢出。

判斷廢棄常量的方法:若是常量池中的某個常量沒有被任何引用所引用,則該常量是廢棄常量。

判斷無用的類:

(1).該類的全部實例都已經被回收,即java堆中不存在該類的實例對象。

(2).加載該類的類加載器已經被回收。

(3).該類所對應的java.lang.Class對象沒有任何地方被引用,沒法在任何地方經過反射機制訪問該類的方法。

Java中經常使用的垃圾收集算法:

(1).標記-清除算法:

最基礎的垃圾收集算法,算法分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成以後統一回收掉全部被標記的對象。

標記-清除算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除以後會產生大量的不連續的內存碎片,空間碎片太多會致使當程序須要爲較大對象分配內存時沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。

(2).複製算法:

將可用內存按容量分紅大小相等的兩塊,每次只使用其中一塊,當這塊內存使用完了,就將還存活的對象複製到另外一塊內存上去,而後把使用過的內存空間一次清理掉。這樣使得每次都是對其中一塊內存進行回收,內存分配時不用考慮內存碎片等複雜狀況,只須要移動堆頂指針,按順序分配內存便可,實現簡單,運行高效。

複製算法的缺點顯而易見,可以使用的內存降爲原來一半。

(3).標記-整理算法:

標記-整理算法在標記-清除算法基礎上作了改進,標記階段是相同的標記出全部須要回收的對象,在標記完成以後不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,在移動過程當中清理掉可回收的對象,這個過程叫作整理。

標記-整理算法相比標記-清除算法的優勢是內存被整理之後不會產生大量不連續內存碎片問題。

複製算法在對象存活率高的狀況下就要執行較多的複製操做,效率將會變低,而在對象存活率高的狀況下使用標記-整理算法效率會大大提升。

(4).分代收集算法:

根據內存中對象的存活週期不一樣,將內存劃分爲幾塊,java的虛擬機中通常把內存劃分爲新生代和年老代,當新建立對象時通常在新生代中分配內存空間,當新生代垃圾收集器回收幾回以後仍然存活的對象會被移動到年老代內存中,當大對象在新生代中沒法找到足夠的連續內存時也直接在年老代中建立。

如今的Java虛擬機就聯合使用了分代複製、標記-清除和標記-整理算法,java虛擬機垃圾收集器關注的內存結構以下:

堆內存被分紅新生代和年老代兩個部分,整個堆內存使用分代複製垃圾收集算法。

(1).新生代:

新生代使用複製和標記-清除垃圾收集算法,研究代表,新生代中98%的對象是朝生夕死的短生命週期對象,因此不須要將新生代劃分爲容量大小相等的兩部份內存,而是將新生代分爲Eden區,Survivor from和Survivor to三部分,其佔新生代內存容量默認比例分別爲8:1:1,其中Survivor from和Survivor to總有一個區域是空白,只有Eden和其中一個Survivor總共90%的新生代容量用於爲新建立的對象分配內存,只有10%的Survivor內存浪費,當新生代內存空間不足須要進行垃圾回收時,仍然存活的對象被複制到空白的Survivor內存區域中,Eden和非空白的Survivor進行標記-清理回收,兩個Survivor區域是輪換的。

新生代中98%狀況下空白Survivor均可以存放垃圾回收時仍然存活的對象,2%的極端狀況下,若是空白Survivor空間沒法存放下仍然存活的對象時,使用內存分配擔保機制,直接將新生代依然存活的對象複製到年老代內存中,同時對於建立大對象時,若是新生代中無足夠的連續內存時,也直接在年老代中分配內存空間。

Java虛擬機對新生代的垃圾回收稱爲Minor GC,次數比較頻繁,每次回收時間也比較短。

使用java虛擬機-Xmn參數能夠指定新生代內存大小。

(2).年老代:

年老代中的對象通常都是長生命週期對象,對象的存活率比較高,所以在年老代中使用標記-整理垃圾回收算法。

Java虛擬機對年老代的垃圾回收稱爲MajorGC/Full GC,次數相對比較少,每次回收的時間也比較長。

當新生代中無足夠空間爲對象建立分配內存,年老代中內存回收也沒法回收到足夠的內存空間,而且新生代和年老代空間沒法在擴展時,堆就會產生OutOfMemoryError異常。

java虛擬機-Xms參數能夠指定最小內存大小,-Xmx參數能夠指定最大內存大小,這兩個參數分別減去Xmn參數指定的新生代內存大小,能夠計算出年老代最小和最大內存容量。

(3).永久代:

java虛擬機內存中的方法區在Sun HotSpot虛擬機中被稱爲永久代,是被各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯後的代碼等數據。永久代垃圾回收比較少,效率也比較低,可是也必須進行垃圾回收,不然會永久代內存不夠用時仍然會拋出OutOfMemoryError異常。

永久代也使用標記-整理算法進行垃圾回收,java虛擬機參數-XX:PermSize和-XX:MaxPermSize能夠設置永久代的初始大小和最大容量。

相關文章
相關標籤/搜索