垃圾回收基礎(chǔ)
JVM能自動(dòng)對(duì)內(nèi)存中已死亡或長(zhǎng)時(shí)間未使用的對(duì)象進(jìn)行清除和回收急迂,從而有效提高內(nèi)存空間利用率鸽嫂。
但如果完全交由JVM來回收對(duì)象就會(huì)增加回收性能的不確定性米罚,所以面對(duì)特定的業(yè)務(wù)場(chǎng)景就需要人為介入來實(shí)現(xiàn)垃圾回收的調(diào)優(yōu)。比如說對(duì)內(nèi)存要求苛刻的情況下,需要提高對(duì)象的回收效率层亿;在CPU使用率高的情況下桦卒,就需要降低并發(fā)時(shí)垃圾回收的頻率。所以說垃圾回收調(diào)優(yōu)是一項(xiàng)必備技能匿又。學(xué)習(xí)調(diào)優(yōu)技能前我們要先了解一下垃圾回收機(jī)制方灾。
從以下三個(gè)角度來了解垃圾回收機(jī)制
1. 回收發(fā)生在哪里
JVM的內(nèi)存區(qū)域主要有5個(gè)部分:方法區(qū)、堆碌更,虛擬機(jī)棧迎吵、本地方法棧、程序計(jì)數(shù)器针贬。
其中線程共享的是方法區(qū)和堆击费,這兩個(gè)區(qū)域是垃圾回收的關(guān)注的區(qū)域。方法區(qū)主要回收的是廢棄的常量和無用類桦他。堆主要是對(duì)象的回收蔫巩,堆是我們要關(guān)注的垃圾回收的重點(diǎn)區(qū)域。
線程隔離的是虛擬機(jī)棧快压、本地方法棧和程序計(jì)數(shù)器圆仔,他們是線程私有的,會(huì)隨著線程的創(chuàng)建而創(chuàng)建蔫劣,銷毀而銷毀坪郭,所以這三個(gè)區(qū)域的內(nèi)存分配和銷毀都是具有確定性的,因此不是垃圾回收關(guān)注的重點(diǎn)脉幢。
2. (堆中的)對(duì)象在什么時(shí)候可以被回收
對(duì)象不再被引用時(shí)即可被回收歪沃。
如何判斷對(duì)象不再被引用?
- 引用計(jì)數(shù)法
通用一個(gè)對(duì)象的引用計(jì)數(shù)器來判斷是否被引用嫌松。每當(dāng)被引用時(shí)沪曙,計(jì)數(shù)器加一,引用失效時(shí)萎羔,計(jì)數(shù)器減一液走,當(dāng)計(jì)數(shù)器為0時(shí),說明不再被引用贾陷,可以被回收了缘眶。
他存在一個(gè)問題,存在對(duì)象之間相互循環(huán)引用的問題髓废,這種情況下相互引用的兩個(gè)對(duì)象的計(jì)數(shù)器都是1巷懈,垃圾回收器就無法判斷進(jìn)行回收了。而他倆都不是被其他對(duì)象引用的對(duì)象瓦哎,所以說也是垃圾砸喻。由于循環(huán)引用的問題柔逼,主流的垃圾回收器是不使用這種回收算法的。 - 可達(dá)性分析算法
以HotSpot為例割岛,它使用了可達(dá)性分析算法愉适。這種算法的基礎(chǔ)依賴于一些叫做gc roots的對(duì)象。
gc roots對(duì)象:虛擬機(jī)棧中引用的對(duì)象癣漆,方法區(qū)中靜態(tài)屬性引用的對(duì)象维咸,方法區(qū)中常量引用的對(duì)象,本地方法棧中JNI引用的對(duì)象惠爽。
以這些gc roots對(duì)象為起點(diǎn)癌蓖,向下搜索所有走過的路徑,那些能與gc roots對(duì)象聯(lián)通的對(duì)象就是存活對(duì)象婚肆。gc roots到該節(jié)點(diǎn)不可達(dá)租副,即為垃圾對(duì)象,可以被回收较性。
3.怎樣回收
垃圾回收算法
- 標(biāo)記清除算法
先對(duì)可回收的對(duì)象進(jìn)行標(biāo)記用僧,然后再清理掉。
他有一個(gè)很嚴(yán)重的問題赞咙,會(huì)產(chǎn)生大量?jī)?nèi)存碎片導(dǎo)致我們無法申請(qǐng)到一個(gè)比較大的連續(xù)的內(nèi)存空間责循。 - 復(fù)制算法
是標(biāo)記清除算法演進(jìn)而來,解決了內(nèi)存碎片的問題攀操。
他將內(nèi)存空間分為兩塊院仿,每次都使用其中的一塊,當(dāng)一塊使用完速和,就將上面還活著的對(duì)象復(fù)制到另一塊內(nèi)存上歹垫,然后把已使用的內(nèi)存一次全部清除掉從而保證內(nèi)存空間的連續(xù)可用。
它也有一個(gè)明顯的缺點(diǎn)健芭,就是把內(nèi)存空間拆分成兩塊县钥。如果你有一個(gè)10g的內(nèi)存秀姐,那你能分配的最大單個(gè)對(duì)象就只有5g慈迈。 - 標(biāo)記整理算法
他的標(biāo)記過程與標(biāo)記清除算法一樣,但回收時(shí)不會(huì)直接對(duì)可回收對(duì)象進(jìn)行回收清理省有,而是讓所有存活的對(duì)象都向一端移動(dòng)痒留,然后再清理掉端邊界以外的區(qū)域。
他也有一個(gè)致命缺點(diǎn)蠢沿,就是效率問題伸头。他對(duì)內(nèi)存變動(dòng)更頻繁,需要整理所有存活對(duì)象的引用地址舷蟀,效率比復(fù)制算法差很多恤磷。 - 分代收集算法
在融合了以上三種算法后誕生面哼。他其實(shí)是一套組合拳。根據(jù)對(duì)象存活周期的不同扫步,將Java堆劃分為新生代和老年代魔策。根據(jù)各個(gè)年代的特點(diǎn)使用最適合的收集算法。在新生代河胎,每次回收需要有大批對(duì)象死去闯袒,只有少量存活,就使用三種算法中效率最高的復(fù)制算法了游岳。而老年代政敢,由于存活對(duì)象比例比較高,沒有額外空間進(jìn)行分配擔(dān)保胚迫,就必須使用標(biāo)記清理算法或標(biāo)記整理算法來回收喷户。