垃圾回收(GC)是JVM的一大殺器,它使程序員可以更高效地專注于程序的開發(fā)設(shè)計(jì),而不用過多地考慮對(duì)象的創(chuàng)建銷毀等操作。但是這并不是說程序員不需要了解GC。GC只是Java編程中一項(xiàng)自動(dòng)化工具,任何一個(gè)工具都有它適用的范圍,當(dāng)超出它的范圍的時(shí)候,可能它將不是那么自動(dòng),而是需要人工去了解與適應(yīng)地適用。
擁有一定工作年限的程序員,在工作期間肯定會(huì)經(jīng)常碰到像內(nèi)存溢出、內(nèi)存泄露、高并發(fā)的場景。這時(shí)候在應(yīng)對(duì)這些問題或場景時(shí),如果對(duì)GC不了解,很可能會(huì)成為個(gè)人的發(fā)展瓶頸。
接下來的兩文將詳細(xì)學(xué)習(xí)下JVM中垃圾回收(GC)的各個(gè)知識(shí)要點(diǎn)。本文先從GC的算法開始先了解,鋪墊好基礎(chǔ),下一篇再詳細(xì)講JVM具體的GC實(shí)現(xiàn)。
GC對(duì)象搜索算法垃圾回收,第一件事就是要搞清楚哪些東西是垃圾,而后才能對(duì)這些垃圾進(jìn)行回收。
那么有什么辦法識(shí)別對(duì)象是否為無用的垃圾呢?狹義地,怎么判斷對(duì)象是否沒被引用呢?
通常有以下兩種算法去識(shí)別判斷
引用計(jì)數(shù)算法
這個(gè)算法非常簡單。給對(duì)象一個(gè)計(jì)數(shù)器,每當(dāng)這個(gè)對(duì)象被引用了,計(jì)數(shù)器值加一;引用失效,則減一。但這個(gè)對(duì)象計(jì)數(shù)值為0的時(shí)候,證明是無用對(duì)象,可以被GC程序回收掉。這種算法比較廣泛應(yīng)用在一些腳本語言上,如FLASH、PYTHON等。
但是引用計(jì)數(shù)算法無法解決對(duì)象間相互引用的問題。當(dāng)a對(duì)象引用了b對(duì)象,b對(duì)象也引用了a對(duì)象,這樣a、b兩個(gè)對(duì)象的計(jì)數(shù)器值都不會(huì)為0,即使這兩個(gè)對(duì)象都被其他對(duì)象所引用,最終導(dǎo)致這些對(duì)象一直無法被回收。這種情況往往會(huì)出現(xiàn)在比較復(fù)雜的編程語言中。
可達(dá)性分析算法
可達(dá)性分析算法(GC roots算法),廣泛應(yīng)用于主流的商用語言。設(shè)置一個(gè)根節(jié)點(diǎn),從圖論角度來看,只要從該節(jié)點(diǎn)可達(dá)一個(gè)對(duì)象,證明這個(gè)對(duì)象是存活的(被引用)。
通常地,GC會(huì)包含以下區(qū)域的對(duì)象:
虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象;
方法區(qū)中類靜態(tài)屬性引用的對(duì)象;
方法區(qū)中常量引用的對(duì)象;
本地方法棧中JNI(即一般說的Native方法)引用的對(duì)象;
垃圾回收算法了解完垃圾是怎么找出來后,接下來看看它們是怎么被清除的。以下介紹幾種清除的算法。
標(biāo)記-清除算法(Mark-Sweep)標(biāo)記-清除,顧名思義,先標(biāo)記垃圾,再清除。它是GC最基礎(chǔ)的算法,后續(xù)很多算法都是基于它上面去改進(jìn)的。
標(biāo)記的過程在上面搜索GC對(duì)象已經(jīng)介紹過了。被標(biāo)記的對(duì)象,在統(tǒng)一GC的時(shí)候會(huì)把標(biāo)記的對(duì)象清除掉。這個(gè)算法比較簡單,不做過多贅述。
這個(gè)算法有一個(gè)很明顯的缺點(diǎn),就是在垃圾回收后會(huì)產(chǎn)生大量不連續(xù)的碎片空間,導(dǎo)致程序要申請(qǐng)較大的對(duì)象時(shí)常無法找到合適的內(nèi)存空間,迫使再次GC。
復(fù)制算法的存在,正是為了解決內(nèi)存碎片問題。并且這個(gè)算法也是分代算法的基礎(chǔ)。
將內(nèi)存分為大小相等的兩塊,每次程序只使用其中一塊,當(dāng)GC發(fā)生的時(shí)候,把存活的對(duì)象復(fù)制到另外一塊內(nèi)存中,整齊的排列,然后清空原來的那塊內(nèi)存。
可以看到,這種算法有點(diǎn)新生代轉(zhuǎn)移到老年代的感覺。
缺點(diǎn):
把內(nèi)存可使用的空間減少了一半,造成空間的浪費(fèi)。
對(duì)象存活數(shù)量較多的時(shí)候,復(fù)制性能比較差
這種缺點(diǎn),在老年代中,對(duì)象存活率比較高的場景下是非常場景間。
標(biāo)記-整理算法(Mark-Compact)針對(duì)復(fù)制算法的兩個(gè)缺點(diǎn),在老年代一般會(huì)用這種標(biāo)記-整理算法。
把存活的對(duì)象移到內(nèi)存的一段,然后把剩余的空間全部清空掉。
分代算法并不是一個(gè)特定的算法,也沒有什么新的內(nèi)容。而是把內(nèi)存分成多個(gè)區(qū)域,一般為新生代、老年代等。然后根據(jù)不同區(qū)域不同的特點(diǎn),用不同回收算法去回收垃圾。
例如新生代,對(duì)象存活率低,比較適用復(fù)制算法。老年代存活率高,比較適用Mark-Compact算法。
目前幾乎所有的商業(yè)虛擬機(jī)都是采用分代收集的。具體不同的收集器在下一文再詳細(xì)說明。
更多技術(shù)文章、精彩干貨,請(qǐng)關(guān)注
博客:zackku.com
微信公眾號(hào):Zack說碼
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77297.html
摘要:上,數(shù)據(jù)按有限大小的包傳輸,這些包成為數(shù)據(jù)報(bào),每個(gè)數(shù)據(jù)報(bào)包含一個(gè)首部和一個(gè)有效載荷。不過,由于數(shù)據(jù)報(bào)長度有限,通常必須將數(shù)據(jù)分解為多個(gè)包,再在目的地重新組合。這兩個(gè)構(gòu)造函數(shù),在返回之前會(huì)與遠(yuǎn)程主機(jī)建立一個(gè)活動(dòng)的網(wǎng)絡(luò)連接。 Internet上,數(shù)據(jù)按有限大小的包傳輸,這些包成為數(shù)據(jù)報(bào)(datagram),每個(gè)數(shù)據(jù)報(bào)包含一個(gè)首部(header)和一個(gè)有效載荷(payload)。首部包含包發(fā)...
摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
閱讀 3240·2021-11-02 14:44
閱讀 3739·2021-09-02 15:41
閱讀 1682·2019-08-29 16:57
閱讀 1801·2019-08-26 13:38
閱讀 3310·2019-08-23 18:13
閱讀 2123·2019-08-23 15:41
閱讀 1685·2019-08-23 14:24
閱讀 3042·2019-08-23 14:03