二 JAVA垃圾收集器
垃圾收集簡史
垃圾收集提供了內存管理的機制使得應用程序不需要在關注內存如何釋放內存用完後垃圾收集會進行收集這樣就減輕了因為人為的管理內存而造成的錯誤比如在C++語言裡出現內存洩露時很常見的Java語言是目前使用最多的依賴於垃圾收集器的語言但是垃圾收集器策略從世紀年代就已經流行起來了比如SmalltalkEiffel等編程語言也集成了垃圾收集器的機制
常見的垃圾收集策略
所有的垃圾收集算法都面臨同一個問題那就是找出應用程序不可到達的內存塊將其釋放這裡面得不可到達主要是指應用程序已經沒有內存塊的引用了而在JAVA中某個對象對應用程序是可到達的是指這個對象被根(根主要是指類的靜態變量或者活躍在所有線程棧的對象的引用)引用或者對象被另一個可到達的對象引用
Reference Counting(引用計數)
引用計數是最簡單直接的一種方式這種方式在每一個對象中增加一個引用的計數這個計數代表當前程序有多少個引用引用了此對象如果此對象的引用計數變為那麼此對象就可以作為垃圾收集器的目標對象來收集
優點
簡單直接不需要暫停整個應用
缺點
需要編譯器的配合編譯器要生成特殊的指令來進行引用計數的操作比如每次將對象賦值給新的引用或者者對象的引用超出了作用域等
不能處理循環引用的問題
跟蹤收集器
跟蹤收集器首先要暫停整個應用程序然後開始從根對象掃描整個堆判斷掃描的對象是否有對象引用這裡面有三個問題需要搞清楚
.如果每次掃描整個堆那麼勢必讓GC的時間變長從而影響了應用本身的執行因此在JVM裡面采用了分代收集在新生代收集的時候minor gc只需要掃描新生代而不需要掃描老生代
.JVM采用了分代收集以後minor gc只掃描新生代但是minor gc怎麼判斷是否有老生代的對象引用了新生代的對象JVM采用了卡片標記的策略卡片標記將老生代分成了一塊一塊的劃分以後的每一個塊就叫做一個卡片JVM采用卡表維護了每一個塊的狀態當JAVA程序運行的時候如果發現老生代對象引用或者釋放了新生代對象的引用那麼就JVM就將卡表的狀態設置為髒狀態這樣每次minor gc的時候就會只掃描被標記為髒狀態的卡片而不需要掃描整個堆具體如下圖
.GC在收集一個對象的時候會判斷是否有引用指向對象在JAVA中的引用主要有四種Strong referenceSoft referenceWeak referencePhantom reference
◆ Strong Reference
強引用是JAVA中默認采用的一種方式我們平時創建的引用都屬於強引用如果一個對象沒有強引用那麼對象就會被回收
public void testStrongReference(){
Object referent = new Object();
Object strongReference = referent;
referent = null;
Systemgc();
assertNotNull(strongReference);
}
◆ Soft Reference
軟引用的對象在GC的時候不會被回收只有當內存不夠用的時候才會真正的回收因此軟引用適合緩存的場合這樣使得緩存中的對象可以盡量的再內存中待長久一點
Public void testSoftReference(){
String str = test;
SoftReference<String> softreference = new SoftReference<String>(str);
str=null;
Systemgc();
assertNotNull(softreferenceget());
}
◆ Weak reference
弱引用有利於對象更快的被回收假如一個對象沒有強引用只有弱引用那麼在GC後這個對象肯定會被回收
Public void testWeakReference(){
String str = test;
WeakReference<String> weakReference = new WeakReference<String>(str);
str=null;
Systemgc();
assertNull(weakReferenceget());
}
◆ Phantom reference
MarkSweep Collector(標記清除收集器)
標記清除收集器最早由Lisp的發明人於年提出標記清除收集器停止所有的工作從根掃描每個活躍的對象然後標記掃描過的對象標記完成以後清除那些沒有被標記的對象
優點
解決循環引用的問題
不需要編譯器的配合從而就不執行額外的指令
缺點
.每個活躍的對象都要進行掃描收集暫停的時間比較長
Copying Collector(復制收集器)復制收集器將內存分為兩塊一樣大小空間某一個時刻只有一個空間處於活躍的狀態當活躍的空間滿的時候GC就會將活躍的對象復制到未使用的空間中去原來不活躍的空間就變為了活躍的空間復制收集器具體過程可以參考下圖
優點
只掃描可以到達的對象不需要掃描所有的對象從而減少了應用暫停的時間
缺點
.需要額外的空間消耗某一個時刻總是有一塊內存處於未使用狀態
.復制對象需要一定的開銷
MarkCompact Collector(標記整理收集器)標記整理收集器汲取了標記清除和復制收集器的優點它分兩個階段執行在第一個階段首先掃描所有活躍的對象並標記所有活躍的對象第二個階段首先清除未標記的對象然後將活躍的的對象復制到堆得底部標記整理收集器的過程示意圖請參考下圖Markcompact策略極大的減少了內存碎片並且不需要像Copy Collector一樣需要兩倍的空間
JVM的垃圾收集策略
GC的執行時要耗費一定的CPU資源和時間的因此在JDK以後JVM引入了分代收集的策略其中對新生代采用MarkCompact策略而對老生代采用了MarkSweep的策略其中新生代的垃圾收集器命名為minor gc老生代的GC命名為Full Gc 或者Major GC其中用Systemgc()強制執行的是Full Gc
Serial Collector
Serial Collector是指任何時刻都只有一個線程進行垃圾收集這種策略有一個名字stop the whole world它需要停止整個應用的執行這種類型的收集器適合於單CPU的機器
Serial Copying Collector
此種GC用XX:UseSerialGC選項配置它只用於新生代對象的收集以後XX:MaxTenuringThreshold來設置對象復制的次數當eden空間不夠的時候GC會將eden的活躍對象和一個名叫From survivor空間中尚不夠資格放入Old代的對象復制到另外一個名字叫To Survivor的空間而此參數就是用來說明到底From survivor中的哪些對象不夠資格假如這個參數設置為那麼也就是說只有對象復制次以後才算是有資格的對象這裡需要注意幾個個問題
◆ From Survivor和To survivor的角色是不斷的變化的同一時間只有一塊空間處於使用狀態這個空間就叫做From Survivor區當復制一次後角色就發生了變化
◆ 如果復制的過程中發現To survivor空間已經滿了那麼就直接復制到old generation
◆ 比較大的對象也會直接復制到Old generation在開發中我們應該盡量避免這種情況的發生
Serial MarkCompact Collector
串行的標記整理收集器是JDK update之前默認的老生代的垃圾收集器此收集使得內存碎片最少化但是它需要暫停的時間比較長
Parallel Collector
Parallel Collector主要是為了應對多CPU大數據量的環境Parallel Collector又可以分為以下兩種
Parallel Copying Collector
此種GC用XX:UseParNewGC參數配置它主要用於新生代的收集此GC可以配合CMS一起使用以後Parallel MarkCompact Collector此種GC用XX:UseParallelOldGC參數配置此GC主要用於老生代對象的收集
Parallel scavenging Collector
此種GC用XX:UseParallelGC參數配置它是對新生代對象的垃圾收集器但是它不能和CMS配合使用它適合於比較大新生代的情況此收集器起始於jdk 它比較適合於對吞吐量高於暫停時間的場合Serial gc和Parallel gc可以用如下的圖來表示
Concurrent Collector
Concurrent Collector通過並行的方式進行垃圾收集這樣就減少了垃圾收集器收集一次的時間這種GC在實時性要求高於吞吐量的時候比較有用此種GC可以用參數XX:UseConcMarkSweepGC配置此GC主要用於老生代和Perm代的收集
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25913.html