自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文帶你了解經(jīng)典的 Java 垃圾回收機(jī)制

新聞 前端
在 Java 8 中,HotSpot 虛擬機(jī)的默認(rèn)垃圾回收器是 ParallelOld。在 Java 11 中,默認(rèn)回收器變成了 G1。

 [[326097]]

在 Java 8 中,HotSpot 虛擬機(jī)的默認(rèn)垃圾回收器是 ParallelOld。在 Java 11 中,默認(rèn)回收器變成了 G1。

注意:從技術(shù)上講,回收器的切換是在 Java 9 中進(jìn)行的,但 G1 的主要增強(qiáng)是在 Java 10 和 11 中完成的。但實(shí)際上,很少有公司使用 Java LTS 以外的版本。

在本文中,我們將討論垃圾回收理論的一些基礎(chǔ)知識(shí),以及這些理論在 HotSpot 中是如何實(shí)現(xiàn)的。這也將解釋為什么要切換 Java 的默認(rèn)垃圾回收器,以及 Java 垃圾回收方法在近來(lái)發(fā)生的一些變化。

1. 基本概念

垃圾回收是系統(tǒng)的一種“清理”活動(dòng),獨(dú)立于應(yīng)用程序的主處理線程,試圖找出不再被使用的內(nèi)存并將其釋放以便可以繼續(xù)重用。

Dijkstra 對(duì)垃圾回收的定義清晰地指出,引用計(jì)數(shù)是自動(dòng)內(nèi)存管理的一種形式,但不屬于垃圾回收。

引用計(jì)數(shù)會(huì)在程序運(yùn)行時(shí)更新每個(gè)對(duì)象的元數(shù)據(jù)(例如,在對(duì)一個(gè)引用類(lèi)型對(duì)象的某個(gè)字段賦值時(shí))。元數(shù)據(jù)的更新需要在應(yīng)用程序線程上進(jìn)行,因此不能清晰地將其劃分為獨(dú)立的活動(dòng)。

回收算法從 root(一組已知是存活對(duì)象)開(kāi)始,通過(guò)跟蹤指針來(lái)確定存活對(duì)象。

這些跟蹤回收器實(shí)現(xiàn)了圖算法,將堆內(nèi)存劃分為存活的和可回收的。

在現(xiàn)代垃圾回收文獻(xiàn)中,并發(fā)(Concurrent)和并行(Parallel)都被用來(lái)描述回收算法。它們聽(tīng)起來(lái)像是同義詞,但實(shí)際上有著完全不同的含義:

  • 并發(fā)——回收線程獨(dú)立于應(yīng)用程序線程運(yùn)行;
  • 并行——使用多個(gè)線程來(lái)執(zhí)行垃圾回收算法。

它們可以被看成是另外兩個(gè)術(shù)語(yǔ)的對(duì)立面——并發(fā)是 stop-the-world(STW)的對(duì)立面,并行是 single-thread(單線程)的對(duì)立面。

實(shí)際的垃圾回收器分為多個(gè)階段,每個(gè)階段還可能具備多種特征。

例如,某個(gè)階段可能是單線程并發(fā),或者是并行 STW。

注意:并發(fā)回收器比 STW 回收器要復(fù)雜得多。它們?cè)谟?jì)算開(kāi)銷(xiāo)方面要大得多,而且它們的行為還有需要注意的地方。

其他你應(yīng)該知道的垃圾回收術(shù)語(yǔ):

  • Exact——Exact 回收器擁有足夠的類(lèi)型信息,能夠區(qū)分 int 和指針之間的區(qū)別。
  • 驅(qū)逐(Evacuate)——移動(dòng)(驅(qū)逐)存活對(duì)象到內(nèi)存的另一個(gè)區(qū)域。在回收周期結(jié)束時(shí),源內(nèi)存區(qū)域變成空的,可以被重用。
  • 壓縮(Compact)——在回收周期結(jié)束時(shí),存活的對(duì)象被連續(xù)地放在內(nèi)存的前部區(qū)域,剩下的區(qū)域可被重用。

Exact 是一種保守模式,缺乏精確的信息,因此通常會(huì)造成更大的內(nèi)存浪費(fèi)。

一些資料還提到了移動(dòng)回收器——包括壓縮和驅(qū)逐算法。但這兩種類(lèi)型之間的差異太大,把它們組合在一起通常用處不大。

非移動(dòng)回收器被稱(chēng)為就地回收器。這些算法需要知道可用內(nèi)存塊的列表才能夠處理內(nèi)存碎片以及合并可用的內(nèi)存塊。

2. HotSpot 中的一些設(shè)計(jì)考慮

我們從定義開(kāi)始,先來(lái)考慮一些基本的事實(shí):

  • 移動(dòng)回收器分配的對(duì)象在其生命周期期間沒(méi)有穩(wěn)定的內(nèi)存地址。
  • 壓縮回收器可用避免出現(xiàn)內(nèi)存碎片。
  • 驅(qū)逐回收器也可以避免內(nèi)存碎片化,并可以實(shí)現(xiàn)對(duì)存活對(duì)象進(jìn)行部分壓縮。
  • 如果堆只由一個(gè)內(nèi)存池組成,則無(wú)法使用驅(qū)逐算法回收。

分代假設(shè)基于對(duì)面向?qū)ο笙到y(tǒng)運(yùn)行時(shí)行為的觀察,它大致將對(duì)象分為兩類(lèi):短期的臨時(shí)對(duì)象和用于執(zhí)行程序任務(wù)的長(zhǎng)期對(duì)象。

注意:分代回收器并不一定總是比非分代回收器更高效,但幾乎所有的應(yīng)用程序都會(huì)從分代回收器中獲得好處。

回收算法的 mark-sweep-compact(根據(jù) Blackburn 和 McKinley)是這樣定義的:

  • 標(biāo)記(Mark):通過(guò)跟蹤對(duì)象圖來(lái)識(shí)別存活的對(duì)象。
  • 清掃(Sweep):讓存活對(duì)象留在原地,同時(shí)識(shí)別出可釋放的空間。
  • 驅(qū)逐(Evacuate):將存活對(duì)象轉(zhuǎn)移到另一個(gè)內(nèi)存池,以此來(lái)釋放空間。
  • 壓縮(Compact):通過(guò)移動(dòng)同一內(nèi)存池中的存活對(duì)象來(lái)釋放空間。

在分代回收算法中,年輕代回收器和老年代回收器通常使用的是完全不同的算法。

這導(dǎo)致我們很難準(zhǔn)確地對(duì)不同階段采用不同算法的回收器進(jìn)行歸類(lèi)。例如,在 CMS 中,年輕代是通過(guò)驅(qū)逐算法那進(jìn)行回收的,而老年代是通過(guò)標(biāo)記清除算法進(jìn)行回收的,如果并發(fā)回收失?。ɡ缬捎谒槠?,則退回到標(biāo)記壓縮算法。

3. HotSpot 中的年輕代垃圾回收

在 HotSpot 中,傳統(tǒng)的回收器將內(nèi)存劃分為 4 個(gè)內(nèi)存池,分別是 Eden、Survivor 0、Survivor 1 和 Tenured。前三個(gè)被統(tǒng)稱(chēng)為年輕代,Tenured 是老年代。

年輕代空間是在年輕代回收周期中進(jìn)行回收的,使用了并行 STW 驅(qū)逐算法, 將存活的對(duì)象轉(zhuǎn)移到一個(gè)空間。

回收算法在當(dāng)前活動(dòng)的內(nèi)存池中標(biāo)記存活的對(duì)象,然后將其撤到非活動(dòng)的內(nèi)存池中。在回收結(jié)束時(shí),兩個(gè)空間被顛倒過(guò)來(lái)——活動(dòng)的內(nèi)存池變?yōu)榉腔顒?dòng)的(即為空),而非活動(dòng)的變?yōu)榛顒?dòng)的。有時(shí)候這也被叫作“半球”(hemispehric)回收。

半球回收可能會(huì)浪費(fèi)內(nèi)存。單遍算法無(wú)法預(yù)先知道正在回收的內(nèi)存區(qū)域中有多少對(duì)象是存活的。這意味著用于存放驅(qū)逐對(duì)象的區(qū)域必須和被清理的區(qū)域一樣大——因此算法需要兩倍于實(shí)際存活對(duì)象大小的內(nèi)存空間。

它還意味著不管在什么時(shí)候都有一半的空間是空的。這些特點(diǎn)導(dǎo)致它不適用于現(xiàn)代工作負(fù)載的老年代垃圾回收,因?yàn)檫@些老年代的對(duì)象集合可能很大:實(shí)際上,在生產(chǎn)環(huán)境中,HotSpot 回收器不會(huì)使用半球回收算法。

半球回收算法被用于回收年輕代。它非常適用于符合分代假設(shè)的工作負(fù)載——即內(nèi)存區(qū)域里大部分都是垃圾對(duì)象?;厥掌魇芤嬗谶@樣的一個(gè)事實(shí):存活對(duì)象總是從年輕代被提升到老年代。

驅(qū)逐回收器的另一個(gè)主要優(yōu)點(diǎn)是它們處理空閑空間的方式。最簡(jiǎn)單的方法是使用指向空閑空間的指針,當(dāng)存活對(duì)象被驅(qū)逐時(shí),很“自然”地被壓縮。

驅(qū)逐算法是 OpenJDK 年輕代回收器的典型算法,它使用了對(duì)象跟蹤。不過(guò),回收只在一個(gè)階段中進(jìn)行,沒(méi)有單獨(dú)的標(biāo)記、清除或壓縮階段。

4. 分代假設(shè)的后果

對(duì)象的生存期通常是未知的,而且在實(shí)際應(yīng)用程序中會(huì)動(dòng)態(tài)發(fā)生變化。因此,追蹤對(duì)象的實(shí)際生存周期是不可行的。

相反,HotSpot 記錄了對(duì)象在垃圾回收過(guò)程中存活下來(lái)的次數(shù),只需要在對(duì)象頭部的元數(shù)據(jù)里添加幾個(gè)比特的信息,在對(duì)象經(jīng)歷了足夠多的垃圾回收之后,它就會(huì)被移動(dòng)(提升)到更老的一代,由不同的垃圾回收器來(lái)管理。

這種機(jī)制與應(yīng)用程序的內(nèi)存分配速度存在一種有趣的交互。如果分配速度加快,那么年輕代將更快被填滿——但“短命對(duì)象”的預(yù)期壽命(以毫秒為單位)保持不變。

這可能會(huì)導(dǎo)致更多對(duì)象在回收周期中存活下來(lái),從而導(dǎo)致年輕代空間充滿了還沒(méi)有資格提升到老年代的對(duì)象。在這種情況下,JVM 別無(wú)選擇,只能提前提升一些對(duì)象——這導(dǎo)致了“過(guò)早提升”。

很多這樣的對(duì)象實(shí)際上都是短命的,在進(jìn)入老年代后很快就會(huì)消失??上У氖?,JVM 沒(méi)有回收它們的機(jī)制,要等到老年代空間的下一個(gè)回收周期才能回收它們。

5. 垃圾回收算法的復(fù)雜性

開(kāi)發(fā)人員經(jīng)常對(duì)垃圾回收算法進(jìn)行復(fù)雜性分析(有時(shí)候也叫作“大 O”)。然而,在實(shí)踐當(dāng)中,這種做法實(shí)際上并不是很令人滿意。

他們可能天真地認(rèn)為標(biāo)記和壓縮階段的時(shí)間復(fù)雜度與活動(dòng)對(duì)象集合的大小成線性關(guān)系,而清除階段與整個(gè)堆大小成線性關(guān)系。

然而,即使不考慮在實(shí)際實(shí)現(xiàn)當(dāng)中可能無(wú)法清晰地進(jìn)行階段隔離(如上面討論的 HotSpot 年輕代回收器),仍然存在一個(gè)更深層次的問(wèn)題。

垃圾回收本質(zhì)上是一種通用算法。這意味著大 O 分析中的固有假設(shè)——當(dāng)數(shù)據(jù)集增大時(shí),起作用的是限制性行為——是不正確的。

生產(chǎn)環(huán)境中的算法需要在面對(duì)所有可能的輸入和工作負(fù)載表現(xiàn)出可接受的行為。它們的漸近性行為與整體性能是不匹配的。

換句話說(shuō),活動(dòng)對(duì)象集合和堆大小本質(zhì)上是獨(dú)立變化的(例如,不同的對(duì)象圖拓?fù)?。這意味著對(duì)于不同的工作負(fù)載,縮放因子會(huì)產(chǎn)生非常不一樣的效果。

例如,壓縮時(shí)需要復(fù)制字節(jié),因此,盡管壓縮階段在活動(dòng)對(duì)象集合的大小上是呈線性的,但其他因素可能與要移動(dòng)的對(duì)象大小有關(guān)。對(duì)于包含大量元素的大數(shù)組,這種說(shuō)法就更加站不住腳。

對(duì)于各種不同形式的回收算法,還存在一些眾所周知的二級(jí)效應(yīng)。例如,在對(duì)只有少量存活對(duì)象的內(nèi)存區(qū)域(“稀疏堆”)執(zhí)行壓縮時(shí),活動(dòng)的對(duì)象將被合并到更密集的區(qū)域。如果對(duì)象的生命周期很長(zhǎng),那么這個(gè)區(qū)域?qū)τ诤罄m(xù)的回收周期來(lái)說(shuō)就不那么稀疏了。

我們可以看到,與 CMS 之類(lèi)的就地回收器相比,在程序的整個(gè)生命周期中,長(zhǎng)壽對(duì)象將保持稀疏分布。事實(shí)上,隨著時(shí)間的推移,空閑空間將變得越來(lái)越碎片化,空閑內(nèi)存塊列表的管理將變得越來(lái)越昂貴。

總的來(lái)說(shuō),不同回收方法的時(shí)間和空間成本模型是不同的,簡(jiǎn)單的算法復(fù)雜度分析也不是很管用。在 HotSpot 中,如果沒(méi)有足夠的連續(xù)空間,就地回收器最終會(huì)退回到壓縮回收器。

6. 總結(jié)

我們討論了 Java 虛擬機(jī)的垃圾回收機(jī)制。垃圾回收是計(jì)算機(jī)科學(xué)的一個(gè)成熟的領(lǐng)域,HotSpot 的垃圾回收器經(jīng)過(guò)了良好的測(cè)試,可以很好地處理大堆工作負(fù)載。大多數(shù) Java 應(yīng)用程序不需要過(guò)多地?fù)?dān)心垃圾回收行為。

如果對(duì)垃圾回收行為較為敏感,那么深入了解垃圾回收的原則(以及它在 JVM 中是如何實(shí)現(xiàn)的)對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)會(huì)很有幫助。

在最近的 Java 版本中,垃圾回收子系統(tǒng)的改進(jìn)再次成為關(guān)注的熱點(diǎn)。要完全理解這些變化,就要很好地掌握這些基礎(chǔ)知識(shí)。后續(xù)的文章將詳細(xì)討論這些更新,例如,為什么改變了默認(rèn)回收器、這對(duì)升級(jí)到 Java 11 的團(tuán)隊(duì)意味著什么。

 

責(zé)任編輯:張燕妮 來(lái)源: 架構(gòu)頭條
相關(guān)推薦

2021-02-26 05:24:35

Java垃圾回收

2021-10-21 10:01:05

Java選擇排序

2009-06-23 14:15:00

Java垃圾回收

2011-07-04 16:48:56

JAVA垃圾回收機(jī)制GC

2023-06-07 16:00:40

JavaScriptV8語(yǔ)言

2023-08-27 21:29:43

JVMFullGC調(diào)優(yōu)

2011-06-28 12:39:34

Java垃圾回收

2015-06-04 09:38:39

Java垃圾回收機(jī)

2010-10-13 10:24:38

垃圾回收機(jī)制JVMJava

2010-09-25 15:33:19

JVM垃圾回收

2017-03-03 09:26:48

PHP垃圾回收機(jī)制

2017-08-17 15:40:08

大數(shù)據(jù)Python垃圾回收機(jī)制

2017-06-12 17:38:32

Python垃圾回收引用

2021-11-05 15:23:20

JVM回收算法

2010-09-26 14:08:41

Java垃圾回收

2021-12-07 08:01:33

Javascript 垃圾回收機(jī)制前端

2021-05-27 21:47:12

Python垃圾回收

2010-09-16 15:10:24

JVM垃圾回收機(jī)制

2010-09-25 15:26:12

JVM垃圾回收

2024-02-22 17:15:22

JS垃圾回收機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)