經常使用定義
java垃圾回收
在空閒時間以不定時的方式進行垃圾回收,回收的是無任何引用的對象佔據的內存空間而不是對象自己java
觸發主GC(Garbage Collector)的條件
(1)當應用程序空閒時,即沒有應用線程在運行時,GC會被調用。由於GC在優先級最低的線程中進行,因此當應用忙時,GC線程就不會被調用,但如下條件除外。
(2)Java堆內存不足時,GC會被調用。當應用線程在運行,並在運行過程當中建立新對象,若這時內存空間不足,JVM就會強制地調用GC線程,以便回收內存用於新的分配。若GC一次以後仍不能知足內存分配的要求,JVM會再進行兩次GC做進一步的嘗試,若仍沒法知足要求,則 JVM將報「out of memory」的錯誤,Java應用將中止。算法
內存泄露
程序中動態分配內存給一些臨時對象,可是對象不會被GC所回收,它始終佔用內存。即被分配的對象可達但已無用。編程
內存溢出
程序運行過程當中沒法申請到足夠的內存而致使的一種錯誤。服務器
爲何要有垃圾回收機制
Java語言創建了垃圾收集機制,用以跟蹤正在使用的對象和發現並回收再也不使用(引用)的對象。該機制能夠有效防範動態內存分配中可能發生的兩個危險:因內存垃圾過多而引起的內存耗盡,以及不恰當的內存釋放所形成的內存非法引用。最終達到自動釋放內存空間,減輕編程的負擔的目的。markdown
垃圾回收算法
引用計數法(Reference Counting Collector)
引用計數是垃圾收集的早期策略。在這種方法中,堆中每個對象都有一個引用計數。當一個對象被建立了,而且指向該對象的引用被分配給一個變量,這個對象的引用計數被設置爲1。好比新建一個對象A a=new A();而後a被分配給另一個變量b,也就是b=a;那麼對象a的引用計數+1。當任何其餘變量被賦值爲對這個對象的引用時,計數加1。當一個對象的引用超過生存期或者被設置一個新的值時,對象的引用計數減1,好比令b=c,則a的引用計數-1。任何引用計數爲0的對象能夠被當作垃圾收集。當一個對象被垃圾收集的時候,它引用的任何對象計數減1。在這種方法中,一個對象被垃圾收集後可能致使後續其餘對象的垃圾收集行動。好比A a=new A();b=a;當b被垃圾回收之後,a的引用計數變爲0,這樣致使a也被垃圾回收。
優勢
引用計數收集器能夠很快執行,交織在程序的運行之中。這個提醒對於程序不能被長時間打斷的實時環境頗有利。
缺點
引用計數沒法檢測出循環(即兩個或者更多的對象互相引用)。循環的例子如,父對象有一個子對象的引用,子對象又反過來引用父對象。這樣對象用戶都不可能計數爲0,就算它們已經沒法被執行程序的根對象觸及。還有一個壞處就是,每次引用計數的增長或者減小都帶來額外的開銷。多線程
標記-清除算法(Mark-Sweep)
此算法是爲了解決引用計數法帶來的不足問題。垃圾回收器從根集開始掃描,識別出哪些對象可達,哪些對象不可達,並用某種方式標記可達對象,例如對每一個可達對象設置一個或多個位。當掃描結束時,未被標記的對象就是沒法觸及的,從而能夠被收集。併發
複製算法(Copying)
此算法的提出是爲了克服句柄的開銷和解決堆碎片的垃圾回收。算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中。算法每次只處理正在使用中的對象,所以複製成本比較小,同時複製過去之後還能進行相應的內存整理,不過出現「碎片」問題。固然,此算法的缺點也是很明顯的,就是須要兩倍內存空間。 函數
標記-整理算法(Mark-Compact)
此算法結合了「標記-清除」和「複製」兩個算法的優勢。但又爲了解決賦值算法的缺陷,充分利用內存空間,提出了」標記-整理」算法。該算法標記階段和」標記-清除」同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。spa
分代收集算法(Generational Collection)
分代收集算法是目前大部分JVM的垃圾收集器採用的算法。它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠根據不一樣代的特色採起最適合的收集算法。
目前大部分垃圾收集器對於新生代都採起Copying算法,由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和剛纔使用過的Survivor空間。
而因爲老年代的特色是每次回收都只回收少許對象,通常使用的是Mark-Compact算法。
注意,在堆區以外還有一個代就是永久代(Permanet Generation),它用來存儲class類、常量、方法描述等。對永久代的回收主要回收兩部份內容:廢棄常量和無用的類。.net
三.典型的垃圾收集器
經常使用的垃圾回收器
目前的收集器主要有三種:串行收集器、並行收集器、併發收集器。
一. 串行收集器
使用單線程處理全部垃圾回收工做,由於無需多線程交互,因此效率比較高。可是,也沒法使用多處理器的優點,因此此收集器適合單處理器機器。固然,此收集器也能夠用在小數據量(100M左右)狀況下的多處理器機器上。可使用-XX:+UseSerialGC打開。
二. 並行收集器
- 對年輕代進行並行垃圾回收,所以能夠減小垃圾回收時間。通常在多線程多處理器機器上使用。使用-XX:+UseParallelGC.打開。並行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中進行了加強–能夠堆年老代進行並行收集。若是年老代不使用併發收集的話,是使用單線程進行垃圾回收,所以會制約擴展能力。使用-XX:+UseParallelOldGC打開。
- 使用-XX:ParallelGCThreads=設置並行垃圾回收的線程數。此值能夠設置與機器處理器數量相等。
- 此收集器能夠進行以下配置:
- 最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,經過-XX:MaxGCPauseMillis=指定。爲毫秒.若是指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減小應用的吞吐量。
- 吞吐量:吞吐量爲垃圾回收時間與非垃圾回收時間的比值,經過-XX:GCTimeRatio=來設定,公式爲1/(1+N)。例如,-XX:GCTimeRatio=19時,表示5%的時間用於垃圾回收。默認狀況爲99,即1%的時間用於垃圾回收。
三. 併發收集器
能夠保證大部分工做都併發進行(應用不中止),垃圾回收只暫停不多的時間,此收集器適合對響應時間要求比較高的中、大規模應用。使用-XX:+UseConcMarkSweepGC打開。
1. 併發收集器主要減小年老代的暫停時間,他在應用不中止的狀況下使用獨立的垃圾回收線程,跟蹤可達對象。在每一個年老代垃圾回收週期中,在收集初期併發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程當中多個線程同時進行垃圾回收工做。
2. 併發收集器使用處理器換來短暫的停頓時間。在一個N個處理器的系統上,併發收集部分使用K/N個可用處理器進行回收,通常狀況下1<=K<=N/4。
3. 在只有一個處理器的主機上使用併發收集器,設置爲incremental mode模式也可得到較短的停頓時間。
4. 浮動垃圾:因爲在應用運行的同時進行垃圾回收,因此有些垃圾可能在垃圾回收進行完成時產生,這樣就形成了「Floating Garbage」,這些垃圾須要在下次垃圾回收週期時才能回收掉。因此,併發收集器通常須要20%的預留空間用於這些浮動垃圾。
5. Concurrent Mode Failure:併發收集器在應用運行時進行收集,因此須要保證堆在垃圾回收的這段時間有足夠的空間供程序使用,不然,垃圾回收還未完成,堆空間先滿了。這種狀況下將會發生「併發模式失敗」,此時整個應用將會暫停,進行垃圾回收。
6. 啓動併發收集器:由於併發收集在應用運行時進行收集,因此必須保證收集完成以前有足夠的內存空間供程序使用,不然會出現「Concurrent Mode Failure」。經過設置-XX:CMSInitiatingOccupancyFraction=指定還有多少剩餘堆時開始執行併發收集
四. 小結
- 串行處理器:
–適用狀況:數據量比較小(100M左右);單處理器下而且對響應時間無要求的應用。
–缺點:只能用於小型應用 - 並行處理器:
–適用狀況:「對吞吐量有高要求」,多CPU、對應用響應時間無要求的中、大型應用。舉例:後臺處理、科學計算。
–缺點:應用響應時間可能較長 - 併發處理器:
–適用狀況:「對響應時間有高要求」,多CPU、對應用響應時間有較高要求的中、大型應用。舉例:Web服務器/應用服務器、電信交換、集成開發環境。
如何影響java垃圾回收
一般咱們在開發中沒法控制JVM的垃圾回收機制,可是能夠經過編程的手段來影響垃圾回收,目的是讓對象符合垃圾回收條件。
1.將無用對象賦值爲null
2.從新爲引用變量賦值
例如:
Person p = new Person("aaa"); p = new Person("bbb");
這樣,new Person(「aaa」)這個對象就是垃圾了—-符合垃圾回收條件了。
3.讓相互聯繫的對象稱爲「島」對象
Person p1 = new Person("aaa"); Person p2 = new Person("bbb"); Person p3 = new Person("ccc"); p1=p2; p2=p3; p3=p1; p1=null; p2=null; p3=null;
在沒有對p一、p二、p3置null以前,它們之間是一種三角戀關係。分別置null,三角戀關係依然存在,可是三個變量不在使用它們了。三個Person對象就組成了一個孤島,最後死在堆上—-被垃圾回收掉。
4.強制的垃圾回收System.gc()
System.gc() Runtime.getRuntime().gc()
上面的方法用於顯式的通知JVM能夠進行一次垃圾回收,但真正垃圾回收機制具體在什麼時間點開始進行垃圾回收是不可預料的;惟一能保證的是當你內存在極少的狀況,垃圾回收器在程序拋出OutofMemaryException以前運行一次。
finalize()方法
在JVM垃圾回收器收集一個對象以前,通常要求程序調用適當的方法釋放資源,但在沒有明確釋放資源的狀況下,Java提供了缺省機制來終止該對象心釋放資源,這個方法就是finalize()。它的原型爲:
protected void finalize() throws Throwable
finalize()方法的理解:
1.finalize()方法是Object中的方法。
2.finalize()方法會在對象被垃圾回收以前被垃圾回收器調用一次,這是Java語言的一種機制。
3.finalize()方法在任何對象上最多隻會被垃圾回收器調用一次。
在finalize()方法返回以後,對象消失,垃圾收集開始執行。原型中的throws Throwable表示它能夠拋出任何類型的異常。之因此要使用finalize(),是存在着垃圾回收器不能處理的特殊狀況。假定你的對象(並不是使用new方法)得到了一塊「特殊」的內存區域,因爲垃圾回收器只知道那些顯示地經由new分配的內存空間,因此它不知道該如何釋放這塊「特殊」的內存區域,那麼這個時候java容許在類中定義一個由finalize()方法。
finalize()方法使用陷阱:
1.垃圾回收器沒法保證垃圾對象能被回收,所以,finalize()方法也沒法保證運行。建議不要重寫finalize()方法,即便重寫,也不要在finalize()方法中寫關鍵的代碼。
2.finalize()方法中能夠把本身傳遞個別的對象,這樣就不是垃圾了,避免了被回收。可是當下次這個對象又符合垃圾回收的時候,finalize()方法不會被調用第二次了,而是直接被清理掉了。
開發中經常使用的減小GC開銷的措施
(1)不要顯式調用System.gc()
此函數建議JVM進行主GC,雖然只是建議而非必定,但不少狀況下它會觸發主GC,從而增長主GC的頻率,也即增長了間歇性停頓的次數。這裏特別須要說明的是,在代碼中顯示的調用System.gc(),並不必定可以進行GC,這個咱們能夠經過finalize()方法進行驗證,即主動調用System.gc(),並不必定每次都調用finalize()方法。finalize()方法的特徵是在對象被回收以前, 首先調用finalize()方法。
(2)儘可能減小臨時對象的使用
臨時對象在跳出函數調用後,會成爲垃圾,少用臨時變量就至關於減小了垃圾的產生,從而延長了出現上述第二個觸發條件出現的時間,減小了主GC的機會。
(3)對象不用時最好顯式置爲Null
通常而言,爲Null的對象都會被做爲垃圾處理,因此將不用的對象顯式地設爲Null,有利於GC收集器斷定垃圾,從而提升了GC的效率。
(4)儘可能使用StringBuffer,而不用String來累加字符串
因爲String是固定長的字符串對象,累加String對象時,並不是在一個String對象中擴增,而是從新建立新的String對象,如 Str5=Str1+Str2+Str3+Str4,這條語句執行過程當中會產生多個垃圾對象,由於對次做「+」操做時都必須建立新的String對象,但這些過渡對象對系統來講是沒有實際意義的,只會增長更多的垃圾。避免這種狀況能夠改用StringBuffer來累加字符串,因StringBuffer 是可變長的,它在原有基礎上進行擴增,不會產生中間對象。
(5)能用基本類型如Int,Long,就不用Integer,Long對象
基本類型變量佔用的內存資源比相應對象佔用的少得多,若是沒有必要,最好使用基本變量。什麼狀況下須要使用Integer?
(6)儘可能少用靜態對象變量
靜態變量屬於全局變量,不會被GC回收,它們會一直佔用內存。
(7)分散對象建立或刪除的時間
集中在短期內大量建立新對象,特別是大對象,會致使忽然須要大量內存,JVM在面臨這種狀況時,只能進行主GC,以回收內存或整合內存碎片, 從而增長主GC的頻率。集中刪除對象,道理也是同樣的。它使得忽然出現了大量的垃圾對象,空閒空間必然減小,從而大大增長了下一次建立新對象時強制主GC 的機會。
參考連接:
http://blog.csdn.net/zsuguangh/article/details/6429592
http://lavasoft.blog.51cto.com/62575/112126/