1. 何爲GCjava
Java與C語言相比的一個優點是,能夠經過本身的JVM自動分配和回收內存空間。算法
垃圾回收機制是由垃圾收集器Garbage Collection來實現的,GC是後臺一個低優先級的守護進程。在內存中低到必定限度時纔會自動運行,所以垃圾回收的時間是不肯定的。數組
爲什麼要這樣設計:由於GC也要消耗CPU等資源,若是GC執行過於頻繁會對Java的程序的執行產生較大的影響,所以實行不按期的GC。緩存
與GC有關的是:JVM運行時數據區中的堆(對象實例會存儲在這裏)和 gabagecollector方法。安全
垃圾回收GC只能回收經過new關鍵字申請的內存(在堆上),可是堆上的內存並不徹底是經過new申請分配的。還有一些本地方法,這些內存若是不手動釋放,就會致使內存泄露,因此須要在finalize中用本地方法(nativemethod)如free操做等,再使用gc方法。數據結構
2. 何爲垃圾多線程
Java中那些不可達的對象就會變成垃圾。對象之間的引用能夠抽象成樹形結構,經過樹根(GC Roots)做爲起點,從這些樹根往下搜索,搜索走過的鏈稱爲引用鏈。併發
當一個對象到GC Roots沒有任何引用鏈相連時,則證實這個對象爲可回收的對象。spa
能夠做爲GC Roots的主要有如下幾種:.net
(1)棧幀中的本地變量表所引用的對象。
(2)方法區中類靜態屬性和常量引用的對象。
(3)本地方法棧中JNI(Native方法)引用的對象。
3. 四種引用類型
3.1 強引用
Object obj = new Object();
這裏的obj引用即是一個強引用,強引用不會被GC回收。即便拋出OutOfMemoryError錯誤,使程序異常終止。
3.2 軟引用
若是內存空間足夠,垃圾回收器就不會回收它,若是內存空間不足了,就會回收這些對象的內存。軟引用可用來實現內存敏感的高速緩存。
軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
3.3 弱引用
弱引用與軟引用的區別在於:垃圾回收器一旦發現了弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。不過因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些弱引用的對象。
弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
3.4 虛引用
虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器發現一個對象有虛引用時,就會把這個虛引用對象加入到與之關聯的引用隊列中。此時該對象並無被GC回收。而是要等到引用隊列被真正的處理後纔會被回收。
程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。
(因爲Object.finalize()方法的不安全性、低效性,經常使用虛引用完成對象回收前的資源釋放工做。)
這裏特別須要注意:當JVM將虛引用插入到引用隊列的時候,虛引用執行的對象內存仍是存在的。可是PhantomReference並無暴露API返回對象。因此若是我想作清理工做,須要繼承PhantomReference類,以便訪問它指向的對象。如NIO直接內存的自動回收,就使用到了sun.misc.Cleaner。
4. 典型的垃圾回收算法
在肯定了哪些垃圾能夠被回收後,垃圾收集器要作的事情就是開始進行垃圾回收,可是這裏面涉及到一個問題是:如何高效地進行垃圾回收。
下面討論幾種常見的垃圾收集算法。
4.1 Mark-Sweep(標記-清除)算法
標記-清除算法分爲兩個階段:標記階段和清除階段。
標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。
標記-清除算法實現起來比較容易,可是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會致使後續過程當中須要爲大對象分配空間時沒法找到足夠的空間而提早觸發GC。
4.2 Copying(複製)算法
Copying算法將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把第一塊內存上的空間一次清理掉,這樣就不容易出現內存碎片的問題,而且運行高效。
可是該算法致使可以使用的內存縮減到原來的一半。並且,該算法的效率跟存活對象的數目多少有很大的關係,若是存活對象不少,那麼Copying算法的效率將會大大下降。(這也是爲何後面提到的新生代採用Copying算法)
4.2 Mark-Compact(標記-整理)算法
爲了解決Copying算法的缺陷,充分利用內存空間,提出了Mark-Compact算法。
該算法標記階段標記出全部須要被回收的對象,可是在完成標記以後不是直接清理可回收對象,而是將存活的對象都移向一端,而後清理掉端邊界之外的全部內存(只留下存活對象)。
4.4 Generational Collection(分代收集)算法
分代收集算法是目前大部分JVM的垃圾收集器採用的算法。
它的核心思想是將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠在不一樣代的採起不一樣的最適合的收集算法。
目前大部分垃圾收集器對於新生代都採起Copying算法,由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,該算法效率在新生代也較高。可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和A空間。在進行了第一次GC以後,使用的即是Eden space和B空間了,下次GC時會將存活對象複製到A空間,如此反覆循環。
當對象在Survivor區躲過一次GC的話,其對象年齡便會加1,默認狀況下,對象年齡達到15時,就會移動到老年代中。通常來講,大對象會被直接分配到老年代,所謂的大對象是指須要大量連續存儲空間的對象,最多見的一種大對象就是大數組,好比:byte[] data = newbyte[4*1024*1024]。
固然分配的規則並非百分之百固定的,這要取決於當前使用的是哪一種垃圾收集器組合和JVM的相關參數。這些搬運工做都是GC完成的,GC不只負責在Heap中搬運實例,同時負責回收存儲空間。
最後,由於每次回收都只回收少許對象,因此老年代通常使用的是標記整理算法。
注意,在方法區中有一個永久代(Permanet Generation),它用來存儲class類、常量、方法描述等。對永久代的回收主要回收兩部份內容:廢棄常量和無用的類。
有關查看垃圾回收信息的JVM常見配置方式:
最後介紹一下有關堆的JVM常見配置方式:
5. 典型的垃圾回收器
垃圾收集算法是內存回收的理論基礎,而垃圾收集器就是內存回收的具體實現。
下面介紹一下HotSpot(JDK 7)虛擬機提供的幾種垃圾收集器,用戶能夠根據本身的需求組合出各個年代使用的收集器。
1.Serial&Serial Old
Serial和Serial Old收集器是最基本最古老的收集器,是一個單線程收集器,而且在它進行垃圾收集時,必須暫停全部用戶線程。Serial收集器是針對新生代的收集器,採用的是Copying算法,Serial Old收集器是針對老年代的收集器,採用的是Mark-Compact算法。它的優勢是實現簡單高效,可是缺點是會給用戶帶來停頓。
2.ParNew
ParNew收集器是Serial收集器的多線程版本,使用多個線程進行垃圾收集。
3.Parallel Scavenge
Parallel Scavenge收集器是一個新生代的多線程收集器(並行收集器),它在回收期間不須要暫停其餘用戶線程,其採用的是Copying算法,該收集器與前兩個收集器有所不一樣,它主要是爲了達到一個可控的吞吐量。
4.Parallel Old
Parallel Old是Parallel Scavenge收集器的老年代版本(並行收集器),使用多線程和Mark-Compact算法。
5.CMS
CMS(Current Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器,它是一種併發收集器,採用的是Mark-Sweep算法。
6.G1
G1收集器是當今收集器技術發展最前沿的成果,它是一款面向服務端應用的收集器,它能充分利用多CPU、多核環境。所以它是一款並行與併發收集器,而且它能創建可預測的停頓時間模型。
最後介紹一下有關收集器設置的JVM常見配置方式: