日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

实用垃圾收集,第1部分–简介

發(fā)布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实用垃圾收集,第1部分–简介 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
這是我打算寫的一系列博客文章的第一部分,其目的是解釋垃圾回收在現(xiàn)實世界中的工作方式(特別是在JVM中 )。 我將介紹一些我認為對于充分理解垃圾收集對于實際目的是必要的理論,但是將其降至最低。

其動機是在各種情況下(例如在Cassandra郵件列表中)不斷出現(xiàn)與垃圾回收相關(guān)的問題。 嘗試提供幫助時的問題是,在針對特定情況定制的郵件列表回復(fù)中臨時解釋垃圾收集的細微差別是一項過多的工作,而您幾乎沒有關(guān)于這種情況的足夠信息來告訴某人他們的情況特殊問題是由引起的。

我希望本指南將成為我回答這些問題的參考。 我希望它會足夠詳細,以便有用,但易于消化,并且對于廣泛的讀者來說也不夠?qū)W術(shù)性。
我非常感謝您對我需要澄清,改進,徹底淘汰等方面的任何反饋。
這里的許多信息并非特定于Java。 但是,為了避免不斷調(diào)用通用和抽象術(shù)語,我將在可能的地方用Hotspot JVM的具體術(shù)語進行發(fā)言。

為什么有人要關(guān)心垃圾收集器?

這是一個好問題。 完美的垃圾收集器可以在沒有人注意到它存在的情況下完成其工作。 不幸的是,沒有已知的完美的垃圾回收算法。 此外,實際上對于大多數(shù)人可用的垃圾收集器的選擇還限于實際上實施的垃圾收集算法的子集。 (類似地, malloc也不是完美的,并且存在其問題,有多種實現(xiàn)方式具有不同的特性。但是,盡管這是一個有趣的話題,但是本文并未嘗試對比自動和顯式內(nèi)存管理。)

現(xiàn)實情況是,與許多技術(shù)問題一樣,需要權(quán)衡取舍。 根據(jù)經(jīng)驗,如果您使用的是可免費使用的基于Hotspot的JVM:s(Oracle / Sun,OpenJDK), 那么您最關(guān)心的就是垃圾回收器(如果您擔(dān)心延遲) 。 如果您不這樣做,那么垃圾回收器將不會很麻煩–除了可能選擇與默認值不同的最大堆大小之外。

所謂等待時間,是指垃圾收集的暫停時間 。 垃圾收集器有時需要暫停應(yīng)用程序才能完成其某些工作。 這通常被稱為停止這世界的停頓(“世界”是從Java應(yīng)用程序的GC說話的角度,或突變可觀測宇宙(因為它是變異堆,而垃圾收集器試圖收集重要的是要注意,盡管所有實際可用的垃圾收集器都在應(yīng)用程序上施加了世界暫停,但這些暫停的頻率和持續(xù)時間隨垃圾收集器,垃圾收集器設(shè)置和應(yīng)用程序行為的選擇而變化很大。

就像我們將看到的那樣,存在垃圾收集算法,這些算法試圖避免需要在停頓世界的暫停中收集整個堆。 這是一個重要屬性的原因是,如果在任何時候(即使很少)停止應(yīng)用程序以完全收集堆,則應(yīng)用程序所遭受的暫停時間將與堆大小成正比 。 通常,這是您在關(guān)心延遲時要避免的主要事情。 也有其他問題,但這通常是一個大問題。

跟蹤與參考計數(shù)

您可能聽說過正在使用引用計數(shù) (例如,cPython在大多數(shù)垃圾收集工作中都使用了引用計數(shù)方案)。 我不會談?wù)撎?#xff0c;因為它與JVM:s無關(guān),只說兩件事:

  • 計數(shù)垃圾回收的引用具有的一個屬性是,將在刪除最后一個引用時立即知道該對象是不可訪問的。
  • 引用計數(shù)將不會檢測為不可訪問的循環(huán)數(shù)據(jù)結(jié)構(gòu),并且還有其他一些問題使其無法成為所有垃圾收集的最終選擇。

JVM而是使用所謂的跟蹤垃圾收集器。 之所以稱為跟蹤,是因為至少在抽象級別上,識別垃圾的過程涉及獲取根集 (例如堆棧上的局部變量或全局變量之類的東西),并跟蹤從那些對象到直接或間接所有對象的路徑。從所述根集合可以間接到達。 一旦確定了所有可到達的(活動的)對象,就可以通過消除過程來標(biāo)識符合垃圾收集器釋放條件的對象。

基本停止,標(biāo)記,掃動,恢復(fù)

一個非常簡單的跟蹤垃圾收集器使用以下過程工作:

  • 完全暫停應(yīng)用程序。
  • 通過跟蹤對象圖(即,遞歸地遵循引用),標(biāo)記所有可到達的對象(從根集開始,參見上文)。
  • 釋放所有無法訪問的對象。
  • 恢復(fù)應(yīng)用程序。
  • 在單線程環(huán)境中,這很容易想象:負責(zé)分配新對象的調(diào)用將立即返回新對象,或者,如果堆已滿,則啟動上述過程以釋放空間,然后執(zhí)行通過完成分配并返回對象。
    沒有一個JVM垃圾收集器像這樣工作。 但是,最好理解垃圾收集器的這種基本形式,因為可用的垃圾收集器實質(zhì)上是上述過程的優(yōu)化。 JVM不實現(xiàn)這種垃圾回收的兩個主要原因是:

    • 每個垃圾回收暫停將足以收集整個堆。 換句話說,它的延遲很差。
    • 對于幾乎所有現(xiàn)實應(yīng)用程序而言,它都不是執(zhí)行垃圾回收的最有效方法(它具有很高的CPU開銷)。

    壓縮與非壓縮垃圾回收

    垃圾收集器之間的一個重要區(qū)別是它們是否正在壓縮 。 壓縮是指將對象移動(在內(nèi)存中)以將其收集在一個密集的內(nèi)存區(qū)域中,而不是稀疏地散布在較大的區(qū)域中。

    真實世界的類比:考慮一個隨機空間中地板上滿是東西的房間。 拿走所有這些東西并將其緊緊塞在角落里實際上就是將它們壓實。 釋放空間。 記住什么是壓實的另一種方法是,設(shè)想其中的一臺機器會像汽車一樣將其壓實成一塊金屬,從而消除了空氣所占的全部空間,從而比原來的汽車占用更少的空間(但是有人指出,雖然汽車ID遭到破壞,但堆上的物體卻沒有!)。

    相比之下,非緊湊型收集器從不移動對象。 將對象分配到內(nèi)存中的特定位置后,該對象將一直保留在那里或直到釋放為止。
    兩者都有一些有趣的屬性:

    • 執(zhí)行壓縮收集的成本是堆上實時數(shù)據(jù)量的函數(shù)。 如果只有1%的數(shù)據(jù)處于活動狀態(tài),則僅需要壓縮1%的數(shù)據(jù)(復(fù)制到內(nèi)存中)。
    • 相比之下,在非緊湊型收集器中,不再可訪問的對象仍然意味著記賬,因為它們的存儲位置必須保持釋放狀態(tài),以便將來分配使用。
    • 在壓縮收集器中,分配通常是通過“ 碰到指針”方法來完成的。 您有一些空間區(qū)域,并保持當(dāng)前的分配指針。 如果您分配一個n字節(jié)的對象,則只需將該指針加n(我就避免了諸如多線程和暗示的優(yōu)化之類的復(fù)雜性)。
    • 在一個非壓實集電極,分配涉及找到其中使用一些機構(gòu),其依賴于用于跟蹤的空閑存儲器的可用性的確切機制來分配。 為了滿足n字節(jié)的分配,必須找到n字節(jié)可用空間的連續(xù)區(qū)域。 如果找不到一個(因為堆是碎片化的 ,這意味著它由可用空間和分配的空間混合在一起),分配將失敗。

    現(xiàn)實類比:再次考慮您的房間。 假設(shè)您是一個壓縮收集器。 您可以在閑暇時隨意在地板上移動?xùn)|西。 當(dāng)您需要為地板中間的那個大沙發(fā)騰出空間時,可以四處移動其他東西以騰出適當(dāng)大小的沙發(fā)空間。 另一方面,如果您是一個不緊湊的收藏家,那么地板上的所有東西都會被釘牢,并且無法移動。 盡管您有足夠的可用地板空間,但大沙發(fā)可能不適合放置–只有一個單獨的空間不足以容納沙發(fā)。

    分代垃圾收集

    大多數(shù)現(xiàn)實世界中的應(yīng)用程序傾向于對短期對象(即已分配的對象,短暫使用的對象,然后不再引用)執(zhí)行大量分配。 分代垃圾收集器嘗試?yán)么擞^察結(jié)果以提高CPU效率(換句話說,具有更高的吞吐量 )。 (更正式地說,大多數(shù)應(yīng)用程序具有此行為的假設(shè)被稱為弱代假設(shè) 。)

    之所以稱其為“世代”,是因為對象分為幾代 。 收集器之間的細節(jié)會有所不同,但此時的合理近似值是將對象分為兩代:

    • 年輕的一代是最初分配對象的地方。 換句話說,所有物體都始于年輕一代。
    • 老一輩是反對“花錢”的對象,因為他們在年輕一代中度過了一段時間。

    代收集者通常更高效的原因是,他們與老一代分開收集年輕一代。 處于穩(wěn)定狀態(tài)下進行分配的應(yīng)用程序的典型行為是,在收集年輕代時經(jīng)常出現(xiàn)短暫的停頓–不經(jīng)常出現(xiàn),但在老一代填滿并觸發(fā)整個堆(舊的和新的)的完整收集時會出現(xiàn)較長的停頓。 如果查看典型應(yīng)用程序的堆使用情況圖,它將類似于以下內(nèi)容:

    吞吐量收集器使用堆的典型鋸齒行為

    鋸齒狀外觀的出現(xiàn)是年輕一代垃圾收集的結(jié)果。 接近尾聲的時候是老一代人變滿了,而JVM對整個堆進行了完整的收集。 該下降結(jié)束時的堆使用量是該時間點實際活動集的合理近似值。 (注意:這是針對配置為使用默認JVM吞吐量收集器的Cassandra實例運行壓力測試的圖;它不反映Cassandra的即開即用行為。)

    請注意,僅在該圖上的任意時間點選擇“當(dāng)前堆使用情況” 都不會使您了解應(yīng)用程序的內(nèi)存使用情況 。 我不能足夠強調(diào)這一點。 通常認為內(nèi)存“使用”是活動集 ,而不是任何特定時間的堆使用情況。 堆的使用更多取決于垃圾收集器的實現(xiàn)細節(jié)。 應(yīng)用程序的內(nèi)存使用量對堆使用量的唯一影響是,它為堆使用量提供了一個下限 。
    現(xiàn)在,回到為什么代收集者通常更高效的原因。

    假設(shè)我們的假設(shè)應(yīng)用是所有物體中有90% 早逝 ; 換句話說,它們永遠無法生存到足以被提升為老一代的程度。 此外,假設(shè)我們的年輕一代集合實際上是緊湊的(請參閱前面的部分)。 現(xiàn)在收集年輕一代的成本大約是跟蹤和復(fù)制它所包含的對象的10%的成本。 剩下的90%的成本很小。 年輕一代的收藏會在充滿時發(fā)生,并且是世界停下來的停頓。

    幸存的對象的10%可能會立即升級為老一代,或者它們可能在年輕一代中再生存一輪或兩輪(取決于各種因素)。 但是,要理解的重要總體行為是,對象從年輕一代開始,并由于在年輕一代中生存而提升為老一代。

    (精明的讀者可能已經(jīng)注意到,不可能完全分開收集年輕一代–如果舊一代中的對象引用了新一代中的對象該怎么辦?這確實是垃圾收集器必須處理的事情;以后的文章會談?wù)撨@個。)

    優(yōu)化過程很大程度上取決于年輕一代的規(guī)模 。 如果大小太大,則可能太大,以至于與收集它相關(guān)的暫停時間是一個明顯的問題。 如果大小太小,則可能甚至死得很年輕的物體也不會足夠快地死去, 以至于它們死后仍處于年輕一代中。

    回想一下,年輕的一代是在變得飽滿時收集的; 這意味著它越小,收集它的頻率就越高。 進一步回想一下,當(dāng)對象在年輕一代中幸存下來時,它們將被提升為老一代。 如果大多數(shù)對象(盡管早逝)都不會因為其太小而在年輕一代中死亡-他們將被提升到老一代,而代際垃圾收集器試圖進行的優(yōu)化將失敗,而您將承擔(dān)以后在老一代中收集對象的全部費用(加上從年輕一代中復(fù)制對象的前期費用)。

    平行收集

    擁有分代收集器的目的是為了優(yōu)化吞吐量 ; 換句話說,應(yīng)用程序在特定時間內(nèi)完成的工作總量。 副作用是,由于垃圾收集而引起的大多數(shù)暫停也會變得更短。 但是,沒有嘗試消除周期性的完整收集,這意味著完成完整收集所需的暫停時間。

    為了減輕這種情況,吞吐量收集器做了一件值得一提的事情:它是并行的 ,這意味著它同時使用多個CPU內(nèi)核來加速垃圾收集。 確實可以縮短停頓時間,但是您可以走多遠還是有一個限制–即使在線性加速的不現(xiàn)實完美情況下(意味著雙CPU計數(shù)->收集時間的一半),您也會受到數(shù)量的限制系統(tǒng)上的CPU內(nèi)核數(shù)。 如果要收集30 GB的堆,即使使用16個并行線程,也將花費大量時間。

    用垃圾回收的話來說,并行一詞用于表示同時在多個CPU內(nèi)核上工作的收集器。

    增量收集

    垃圾回收上下文中的增量是指將需要完成的工作分成較小的塊,通常目的是將應(yīng)用程序暫停多個短暫的時間而不是一個長時間的暫停。 從年輕的一代收集器構(gòu)成增量功的意義上講,上述一代收集器的行為是部分增量的。 但是,從總體上看,收集過程不是增量的,因為在舊的一代變滿時會發(fā)生全部堆收集。
    其他形式的增量收集也是可能的; 例如,對于應(yīng)用程序執(zhí)行的每個分配,收集器可以執(zhí)行少量的垃圾收集工作。 該概念與特定的實施策略無關(guān)。

    并發(fā)收集

    垃圾回收上下文中的并發(fā)是指與應(yīng)用程序(變異器) 同時執(zhí)行垃圾回收工作。 例如,在8核系統(tǒng)上,垃圾收集器可能保留兩個后臺線程,這些線程在應(yīng)用程序運行時執(zhí)行垃圾收集工作。 這允許完成大量工作而不會導(dǎo)致應(yīng)用程序暫停,通常會以一定的吞吐量和實現(xiàn)復(fù)雜性為代價(對于垃圾收集器實現(xiàn)者)。

    可用的熱點垃圾收集器

    Hotspot中垃圾收集器的默認選擇是吞吐量收集器,它是一個世代的并行壓縮收集器。 完全針對吞吐量進行了優(yōu)化; 在給定時間段內(nèi)應(yīng)用程序完成的工作總量。

    CMS收集器是解決延遲/暫停時間問題的傳統(tǒng)替代方法。 CMS代表并發(fā)標(biāo)記和掃描 ,是指收集器使用的機制。 收集器的目的是最大程度地減少甚至消除長時間的停頓,將垃圾回收工作限制為較短的停頓(通常是并行)停頓,并與應(yīng)用程序同時執(zhí)行更長的工作相結(jié)合。 CMS收集器的一個重要屬性是它不緊湊,因此存在碎片問題(有關(guān)詳細信息,請參閱后面的博客文章)。

    在JDK 1.6和JDK 1.7的更高版本中,有一個新的垃圾收集器,稱為G1 (代表Garbage First )。 像CMS收集器一樣,其目的是嘗試減輕或消除長時間停頓世界停頓的需求,并且它的大部分工作都是在短暫的停頓世界漸進停頓的同時進行的,其中一些工作也在??完成中與應(yīng)用程序同時進行。 與CMS相反,G1 是緊湊的收集器,并且沒有碎片問題的困擾-而是有其他折衷的選擇(同樣,在以后的博客文章中將對此進行更多討論)。

    觀察垃圾收集器行為

    我鼓勵讀者嘗試使用垃圾收集器的行為。 使用jconsole(與JDK一起提供)或VisualVM (在本文較早的時候生成了該圖)來可視化正在運行的JVM上的行為。 但是,尤其要開始運行JVM,以開始熟悉垃圾收集日志的輸出(已更新jbellis的反饋–謝謝!):

    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintGCDateStamps
    • -XX:+PrintGCApplicationStoppedTime
    • -XX:+PrintPromotionFailure

    也有用但冗長(含義在以后的文章中解釋):

    • -XX:+PrintHeapAtGC
    • -XX:+PrintTenuringDistribution
    • -XX:PrintFLSStatistics=1

    對于吞吐量收集器,輸出非常容易讀取。 對于CMS和G1,在沒有介紹的情況下,輸出對于分析而言更加不透明。 我希望在以后的更新中對此進行介紹。

    同時,得出的結(jié)論是,每當(dāng)懷疑與GC相關(guān)的問題時,上面的那些選項可能就是您要使用的第一件事。 當(dāng)人們開始假設(shè)GC問題時,這幾乎總是我告訴人們的第一件事。 您是否看過GC日志? 如果您還沒有,那可能是在浪費時間猜測GC。

    結(jié)論

    我試圖制作一個速成課程介紹,希望對我有啟發(fā)性,但主要是作為后續(xù)文章的背景。 我歡迎任何反饋,尤其是在情況不清楚或我做出太多假設(shè)的情況下。 正如我一開始所說的那樣,我希望這個系列能夠被廣泛的讀者所接受,盡管我當(dāng)然確實具有一定的專業(yè)水平。 但是,不需要垃圾收集方面的知識。 如果是,我已經(jīng)失敗了-請讓我知道。

    參考: 實用垃圾收集,第1部分–我們的JCG合作伙伴 Peter Schuller在(mod:world:scode)博客上的介紹


    翻譯自: https://www.javacodegeeks.com/2012/01/practical-garbage-collection-part-1.html

    總結(jié)

    以上是生活随笔為你收集整理的实用垃圾收集,第1部分–简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。