深入java垃圾回收的詳解
1.垃圾收集算法的核心思想
Java語(yǔ)言建立了垃圾收集機(jī)制,用以跟蹤正在使用的對(duì)象和發(fā)現(xiàn)并回收不再使用(引用)的對(duì)象。該機(jī)制可以有效防范動(dòng)態(tài)內(nèi)存分配中可能發(fā)生的兩個(gè)危險(xiǎn):因內(nèi)存垃圾過多而引發(fā)的內(nèi)存耗盡,以及不恰當(dāng)?shù)膬?nèi)存釋放所造成的內(nèi)存非法引用。
垃圾收集算法的核心思想是:對(duì)虛擬機(jī)可用內(nèi)存空間,即堆空間中的對(duì)象進(jìn)行識(shí)別,如果對(duì)象正在被引用,那么稱其為存活對(duì)象,反之,如果對(duì)象不再被引用,則為垃圾對(duì)象,可以回收其占據(jù)的空間,用于再分配。垃圾收集算法的選擇和垃圾收集系統(tǒng)參數(shù)的合理調(diào)節(jié)直接影響著系統(tǒng)性能,因此需要開發(fā)人員做比較深入的了解。
2.觸發(fā)主GC(Garbage Collector)的條件
JVM進(jìn)行次GC的頻率很高,但因?yàn)檫@種GC占用時(shí)間極短,所以對(duì)系統(tǒng)產(chǎn)生的影響不大。更值得關(guān)注的是主GC的觸發(fā)條件,因?yàn)樗鼘?duì)系統(tǒng)影響很明顯??偟膩碚f,有兩個(gè)條件會(huì)觸發(fā)主GC:
(1)當(dāng)應(yīng)用程序空閑時(shí),即沒有應(yīng)用線程在運(yùn)行時(shí),GC會(huì)被調(diào)用。因?yàn)镚C在優(yōu)先級(jí)最低的線程中進(jìn)行,所以當(dāng)應(yīng)用忙時(shí),GC線程就不會(huì)被調(diào)用,但以下條件除外。
(2)Java堆內(nèi)存不足時(shí),GC會(huì)被調(diào)用。當(dāng)應(yīng)用線程在運(yùn)行,并在運(yùn)行過程中創(chuàng)建新對(duì)象,若這時(shí)內(nèi)存空間不足,JVM就會(huì)強(qiáng)制地調(diào)用GC線程,以便回收內(nèi)存用于新的分配。若GC一次之后仍不能滿足內(nèi)存分配的要求,JVM會(huì)再進(jìn)行兩次GC作進(jìn)一步的嘗試,若仍無法滿足要求,則 JVM將報(bào)“out of memory”的錯(cuò)誤,Java應(yīng)用將停止。
由于是否進(jìn)行主GC由JVM根據(jù)系統(tǒng)環(huán)境決定,而系統(tǒng)環(huán)境在不斷的變化當(dāng)中,所以主GC的運(yùn)行具有不確定性,無法預(yù)計(jì)它何時(shí)必然出現(xiàn),但可以確定的是對(duì)一個(gè)長(zhǎng)期運(yùn)行的應(yīng)用來說,其主GC是反復(fù)進(jìn)行的。
3.減少GC開銷的措施
根據(jù)上述GC的機(jī)制,程序的運(yùn)行會(huì)直接影響系統(tǒng)環(huán)境的變化,從而影響GC的觸發(fā)。若不針對(duì)GC的特點(diǎn)進(jìn)行設(shè)計(jì)和編碼,就會(huì)出現(xiàn)內(nèi)存駐留等一系列負(fù)面影響。為了避免這些影響,基本的原則就是盡可能地減少垃圾和減少GC過程中的開銷。具體措施包括以下幾個(gè)方面:
(1)不要顯式調(diào)用System.gc()
此函數(shù)建議JVM進(jìn)行主GC,雖然只是建議而非一定,但很多情況下它會(huì)觸發(fā)主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數(shù)。這里特別需要說明的是,在代碼中顯示的調(diào)用System.gc(),并不一定能夠進(jìn)行GC,這個(gè)我們可以通過finalize()方法進(jìn)行驗(yàn)證,即主動(dòng)調(diào)用System.gc(),并不一定每次都調(diào)用finalize()方法。finalize()方法的特征是在對(duì)象被回收之前, 首先調(diào)用finalize()方法。
(2)盡量減少臨時(shí)對(duì)象的使用
臨時(shí)對(duì)象在跳出函數(shù)調(diào)用后,會(huì)成為垃圾,少用臨時(shí)變量就相當(dāng)于減少了垃圾的產(chǎn)生,從而延長(zhǎng)了出現(xiàn)上述第二個(gè)觸發(fā)條件出現(xiàn)的時(shí)間,減少了主GC的機(jī)會(huì)。
(3)對(duì)象不用時(shí)最好顯式置為Null
一般而言,為Null的對(duì)象都會(huì)被作為垃圾處理,所以將不用的對(duì)象顯式地設(shè)為Null,有利于GC收集器判定垃圾,從而提高了GC的效率。
(4)盡量使用StringBuffer,而不用String來累加字符串(詳見blog另一篇文章JAVA中String與StringBuffer)
由于String是固定長(zhǎng)的字符串對(duì)象,累加String對(duì)象時(shí),并非在一個(gè)String對(duì)象中擴(kuò)增,而是重新創(chuàng)建新的String對(duì)象,如 Str5=Str1+Str2+Str3+Str4,這條語(yǔ)句執(zhí)行過程中會(huì)產(chǎn)生多個(gè)垃圾對(duì)象,因?yàn)閷?duì)次作“+”操作時(shí)都必須創(chuàng)建新的String對(duì)象,但這些過渡對(duì)象對(duì)系統(tǒng)來說是沒有實(shí)際意義的,只會(huì)增加更多的垃圾。避免這種情況可以改用StringBuffer來累加字符串,因StringBuffer 是可變長(zhǎng)的,它在原有基礎(chǔ)上進(jìn)行擴(kuò)增,不會(huì)產(chǎn)生中間對(duì)象。
(5)能用基本類型如Int,Long,就不用Integer,Long對(duì)象
基本類型變量占用的內(nèi)存資源比相應(yīng)對(duì)象占用的少得多,如果沒有必要,最好使用基本變量。什么情況下需要使用Integer?
(6)盡量少用靜態(tài)對(duì)象變量
靜態(tài)變量屬于全局變量,不會(huì)被GC回收,它們會(huì)一直占用內(nèi)存。
(7)分散對(duì)象創(chuàng)建或刪除的時(shí)間
集中在短時(shí)間內(nèi)大量創(chuàng)建新對(duì)象,特別是大對(duì)象,會(huì)導(dǎo)致突然需要大量?jī)?nèi)存,JVM在面臨這種情況時(shí),只能進(jìn)行主GC,以回收內(nèi)存或整合內(nèi)存碎片, 從而增加主GC的頻率。集中刪除對(duì)象,道理也是一樣的。它使得突然出現(xiàn)了大量的垃圾對(duì)象,空閑空間必然減少,從而大大增加了下一次創(chuàng)建新對(duì)象時(shí)強(qiáng)制主GC 的機(jī)會(huì)。
4.垃圾回收算法
(1)引用計(jì)數(shù)收集器
引用計(jì)數(shù)是垃圾收集的早期策略。在這種方法中,堆中每一個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)。當(dāng)一個(gè)對(duì)象被創(chuàng)建了,并且指向該對(duì)象的引用被分配給一個(gè)變量,這個(gè)對(duì)象的引用計(jì)數(shù)被設(shè)置為1。比如新建一個(gè)對(duì)象A a=new A();然后a被分配給另外一個(gè)變量b,也就是b=a;那么對(duì)象a的引用計(jì)數(shù)+1。當(dāng)任何其他變量被賦值為對(duì)這個(gè)對(duì)象的引用時(shí),計(jì)數(shù)加1。當(dāng)一個(gè)對(duì)象的引用超過生存期或者被設(shè)置一個(gè)新的值時(shí),對(duì)象的引用計(jì)數(shù)減1,比如令b=c,則a的引用計(jì)數(shù)-1。任何引用計(jì)數(shù)為0的對(duì)象可以被當(dāng)做垃圾收集。當(dāng)一個(gè)對(duì)象被垃圾收集的時(shí)候,它引用的任何對(duì)象計(jì)數(shù)減1。在這種方法中,一個(gè)對(duì)象被垃圾收集后可能導(dǎo)致后續(xù)其他對(duì)象的垃圾收集行動(dòng)。比如A a=new A();b=a;當(dāng)b被垃圾回收以后,a的引用計(jì)數(shù)變?yōu)?,這樣導(dǎo)致a也被垃圾回收。
方法的好處:引用計(jì)數(shù)收集器可以很快執(zhí)行,交織在程序的運(yùn)行之中。這個(gè)提醒對(duì)于程序不能被長(zhǎng)時(shí)間打斷的實(shí)時(shí)環(huán)境很有利。
方法的壞處:引用計(jì)數(shù)無法檢測(cè)出循環(huán)(即兩個(gè)或者更多的對(duì)象互相引用)。循環(huán)的例子如,父對(duì)象有一個(gè)子對(duì)象的引用,子對(duì)象又反過來引用父對(duì)象。這樣對(duì)象用戶都不可能計(jì)數(shù)為0,就算它們已經(jīng)無法被執(zhí)行程序的根對(duì)象觸及。還有一個(gè)壞處就是,每次引用計(jì)數(shù)的增加或者減少都帶來額外的開銷。
(2)追蹤收集器
垃圾檢測(cè)通常通過建立一個(gè)根對(duì)象的集合并且檢查從這些根對(duì)象開始的可觸及性來實(shí)現(xiàn)。如果正在執(zhí)行的程序可以訪問到的根對(duì)象和某個(gè)對(duì)象之間存在引用路徑,這個(gè)對(duì)象就是可觸及的。對(duì)于程序來說,根對(duì)象總是可以訪問的。從這些根對(duì)象開始,任何可以被觸及的對(duì)象都是被認(rèn)為是“活動(dòng)”的對(duì)象。無法被觸及的對(duì)象被認(rèn)為是垃圾,因?yàn)樗鼈儾辉谟绊懗绦虻奈磥韴?zhí)行。
跟蹤收集器是追蹤從根結(jié)點(diǎn)開始的對(duì)象引用圖。在追蹤過程中遇到的對(duì)象以某手方式打上標(biāo)記??偟膩碚f,要么在對(duì)象本身上設(shè)置標(biāo)記,要么用一個(gè)獨(dú)立的位圖來設(shè)置標(biāo)記。當(dāng)追蹤結(jié)束時(shí),未被標(biāo)記的對(duì)象就是無法觸及的,從而可以被收集。
基本的追蹤算法被稱作“標(biāo)記并清除”。這個(gè)名字指出垃圾手機(jī)的兩個(gè)階段。在標(biāo)記階段,垃圾收集器遍歷引用樹,標(biāo)記每一個(gè)遇到的對(duì)象。在清除階段,未被標(biāo)記的對(duì)象被釋放,釋放對(duì)象后獲得的內(nèi)存被返回到正在執(zhí)行的程序。在Java虛擬機(jī)中,清除步驟必須包括對(duì)象的終結(jié)。
- 淺析Java內(nèi)存模型與垃圾回收
- 簡(jiǎn)單理解Java的垃圾回收機(jī)制與finalize方法的作用
- Java垃圾回收finalize()作用詳解
- 老生常談Java虛擬機(jī)垃圾回收機(jī)制(必看篇)
- Java文件流關(guān)閉和垃圾回收機(jī)制
- 快速理解Java垃圾回收和jvm中的stw
- Java中垃圾回收器GC對(duì)吞吐量的影響測(cè)試
- Java垃圾回收機(jī)制簡(jiǎn)述
- 簡(jiǎn)單介紹Java垃圾回收機(jī)制
- Java垃圾回收器的方法和原理總結(jié)
- 淺談關(guān)于Java的GC垃圾回收器的一些基本概念
- Java的垃圾強(qiáng)制回收實(shí)例分析
相關(guān)文章
詳解spring cloud config整合gitlab搭建分布式的配置中心
這篇文章主要介紹了詳解spring cloud config整合gitlab搭建分布式的配置中心,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01Josephus環(huán)的四種解法(約瑟夫環(huán))基于java詳解
這篇文章主要介紹了Josephus環(huán)的四種解法(約瑟夫環(huán))基于java詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09Java 發(fā)送http請(qǐng)求上傳文件功能實(shí)例
本文通過實(shí)例代碼給大家介紹了Java 發(fā)送http請(qǐng)求上傳文件功能,需要的朋友參考下吧2017-06-06Java class文件格式之特殊字符串_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
特殊字符串出現(xiàn)在class文件中的常量池中,本著循序漸進(jìn)和減少跨度的原則, 首先把class文件中的特殊字符串做一個(gè)詳細(xì)的介紹, 然后再回過頭來繼續(xù)講解常量池,對(duì)java class 文件格式相關(guān)知識(shí)感興趣的的朋友一起學(xué)習(xí)吧2017-06-06spring boot devtools在Idea中實(shí)現(xiàn)熱部署方法
這篇文章主要介紹了spring boot devtools在Idea中實(shí)現(xiàn)熱部署方法及注意要點(diǎn),需要的朋友可以參考下2018-02-02javaweb實(shí)現(xiàn)注冊(cè)登錄頁(yè)面
這篇文章主要為大家詳細(xì)介紹了javaweb實(shí)現(xiàn)注冊(cè)登錄頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04詳解Java弱引用(WeakReference)的理解與使用
這篇文章主要介紹了Java弱引用(WeakReference)的理解與使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Java正則表達(dá)式之split()方法實(shí)例詳解
這篇文章主要介紹了Java正則表達(dá)式之split()方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了split方法的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-03-03