昨天看到一個面試題虫溜,問的是Java的垃圾回收機制,當(dāng)時想了想牲迫,發(fā)現(xiàn)自己對這塊一知半解耐朴,很難組織系統(tǒng)的語言來描述。既然不是很清楚恩溅,當(dāng)然要趁這個機會好好了解一下隔箍。這種東西自然是人家官網(wǎng)的文檔最為準(zhǔn)確,果斷跑去Oracle網(wǎng)站上脚乡,找到了一篇講Java垃圾回收的基礎(chǔ)教學(xué)篇”醮铮看完之后順便翻譯一下奶稠,以便以后翻閱。
原文地址:
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
什么是自動垃圾回收捡遍?(What is Automatic Garbage Collection锌订?)
自動垃圾回收是針對堆區(qū)內(nèi)存中的對象,分辨哪些對象是正在使用中而哪些對象沒有画株,然后刪除那些沒用的對象辆飘。一個正在使用中的對象或者是一個被引用的對象啦辐,意味著你的程序中的某部分仍然維護著指向這個對象的指針。一個無用的對象或者是一個無引用的對象蜈项,是說它已經(jīng)不再被你的程序中任何一個模塊所引用芹关。所以被一個無用對象所占用的內(nèi)存是可以被回收的。
在其他的編程語言紧卒,如C中侥衬,分配和回收內(nèi)存是用戶手動管理的。但在Java中跑芳,內(nèi)存的回收是由垃圾回收器自動管理的轴总。基本的處理過程如以下的描述博个。
步驟 1:標(biāo)記(Marking)
處理過程的第一步叫做標(biāo)記怀樟。這一步中垃圾回收器會分辨哪些內(nèi)存是使用中,哪些沒有盆佣。
被引用的對象用藍(lán)色表示漂佩。沒有被引用的對象用金黃色表示。在標(biāo)記階段會掃描所有的對象以判斷罪塔。這會是一個非常耗時的處理過程因為系統(tǒng)中所有的對象都需要被掃描投蝉。
步驟 2:正常刪除(Normal Deletion)
正常刪除會刪除那些沒有被引用的對象,保留被引用的對象和指向可用空間的指針征堪。
內(nèi)存分配器會維護指向可用空間的指針瘩缆,以便分配新的對象時使用。
步驟 2a:刪除壓縮(Deletion with Compacting)
為了提高性能佃蚜,除了刪除未被引用的對象之外庸娱,你還可以壓縮保留的被引用對象。通過將被引用的對象合并到一起谐算,這使得新內(nèi)存的分配更加簡單和快速熟尉。
為什么分代垃圾回收?(Why Generational Garbage Collection?)
上文說過洲脂,在一個JVM中標(biāo)記和壓縮所有的對象是很低效率的斤儿。隨著越來越多的對象被分配,對象列表也變得越來越長而垃圾回收的時間就變得越來越長恐锦。然而往果,通過對應(yīng)用程序的經(jīng)驗分析表明大多數(shù)對象的存活時間都很短。
這里是一個例子一铅,Y軸表示已分配的字節(jié)數(shù)陕贮,X軸表示隨著時間變化已分配的字節(jié)數(shù)的變化。
你可以看到潘飘,隨著時間的增加越來越少的對象在占用內(nèi)存肮之。實際上掉缺,大多數(shù)的對象如圖中左邊所示只有很短的生命周期。
JVM分代(JVM Generations)
從對象分配的規(guī)律中學(xué)習(xí)到的信息戈擒,我們可以用以來改善JVM的性能眶明。因此,堆區(qū)被分成更小的部分或者說代(generations)峦甩。主要有:年輕代(Young Generation)赘来、年老代(Old or Tenured Generation)和永久代(Permanent Generation)。
年輕代凯傲,所有新分配和成長中的對象犬辰。當(dāng)年輕代裝滿時,會引起一次小垃圾回收(minor garbage collection)冰单。小回收可以通過提高對象的淘汰率來提高效率幌缝。一個充滿死對象的年輕代可以被非常迅速的回收。一些幸存的對象會變老诫欠,最終會被移到年老代中涵卵。
讓地球停止轉(zhuǎn)到的事件(Stop the World Event)- 所有的小垃圾回收都是“讓地球停止轉(zhuǎn)動”的事件。這意味著荒叼,所有的應(yīng)用線程都會被停止直到這次回收結(jié)束轿偎。小垃圾回收總是“讓地球停止轉(zhuǎn)動”的事件。
年老代被廓,用來存儲長久幸存下來的對象坏晦。一般地,對年輕代的對象設(shè)置一個閾值嫁乘,當(dāng)它的年齡到達(dá)了閾值時昆婿,這個對象就會被轉(zhuǎn)移到年老代中。最終年老代也需要被回收蜓斧。這個事件叫做大垃圾回收(major garbage collection)仓蛆。
大垃圾回收也是一個“讓地球停止轉(zhuǎn)動”的事件。一個大垃圾回收經(jīng)常會更加的緩慢挎春,這是因為它包含了所有活著的對象看疙。所以對于響應(yīng)型應(yīng)用程序(Responsive application)來說,大垃圾回收的次數(shù)要盡可能的少搂蜓。同樣需要注意狼荞,大垃圾回收占用的時間與使用的垃圾回收器的種類也有很大的關(guān)系。
永久代帮碰,包含JVM所需要的用于描述類和方法的元數(shù)據(jù)。永久代是在運行時由JVM根據(jù)應(yīng)用程序中所使用的類來進(jìn)行分配拾积。另外殉挽,Java SE庫的類和方法可能會存儲在這里丰涉。
如果JVM發(fā)現(xiàn)程序不再需要一些類的時候,它會將這些類回收然后將空間分配給其他的類斯碌。在一次全垃圾回收(full garbage collection)中一死,永久代也會被回收。
分代垃圾回收過程(The Generational Garbage Collection Process)
現(xiàn)在你已經(jīng)明白了為什么堆區(qū)會被分為不同的代傻唾,是時候來看一下這些代之間是怎樣聯(lián)系的投慈。以下的圖片講述了JVM中對象的分配和年齡增長的過程。
-
首先冠骄,任何新分配的對象會被分配到伊甸園(eden)中伪煤。兩個幸存者(survivor)空間一開始都是空的。
-
當(dāng)伊甸園被裝滿時凛辣,觸發(fā)一次小垃圾回收抱既。
-
被引用的對象會被轉(zhuǎn)移到第一個幸存者空間中。未被引用的對象會被刪除扁誓。
-
在下一次小垃圾回收時防泵,伊甸園中發(fā)生相同的事情。未被引用的對象被刪除而被引用的對象被移到一個幸存者空間中蝗敢。然后捷泞,這回它們被轉(zhuǎn)移到第二個幸存者空間中(S1)。另外寿谴,在第一個幸存者空間(S0)中仍幸存的對象會年齡增加然后轉(zhuǎn)移到S1中锁右。一旦所有幸存的對象都被移動到S1中,S0和伊甸園就都清理完畢拭卿。注意現(xiàn)在我們現(xiàn)在在幸存者空間中有不同年齡的對象了骡湖。
-
再下一次小垃圾回收,處理流程與上一次一樣峻厚。不過這次幸存者空間調(diào)換了位置响蕴。被引用的對象都被轉(zhuǎn)移到了S0.幸存的對比年齡增加。伊甸園和S1被清理完畢惠桃。
-
這次有了晉級操作浦夷。在一次小垃圾回收后,當(dāng)對象的年齡達(dá)到了閾值(在例子中為8)辜王,它們就從年輕代晉級到了年老代劈狐。
-
由于小垃圾回收不斷地被觸發(fā),就有對象不斷的晉級到年老代呐馆。
-
到目前為止已經(jīng)差不多覆蓋了年輕代所有的處理過程肥缔。最終,在年老代會進(jìn)行一次大垃圾回收來清理和壓縮空間汹来。
PS:看完整個垃圾回收的基礎(chǔ)內(nèi)容之后续膳,很深的一個感受就是命名的時候真的是大有深意啊改艇。有些時候看中文翻譯過來的名詞會感覺很莫名其妙,但是看了原文后就會恍然大悟坟岔。例如本文中涉及到的詞:eden, survivor, yong generation, old generation谒兄。有興趣的可以查一下eden的典故,相信很容易就可以把Java分代垃圾回收這一過程理解為一個講述大自然優(yōu)勝劣汰適者生存的故事社付。