實用垃圾收集,第1部分–簡介

這是我打算寫的一系列博客文章的第一部分,其目的是解釋垃圾回收在現實世界中的工作方式(特別是在JVM中 )。 我將介紹一些我認為對于充分理解垃圾收集對于實際目的是必要的理論,但是將其降至最低。

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

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

為什么有人要關心垃圾收集器?

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

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

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

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

跟蹤與參考計數

您可能聽說過正在使用引用計數 (例如,cPython在大多數垃圾收集工作中都使用了引用計數方案)。 我不會談論太多,因為它與JVM:s無關,只說兩件事:

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

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

基本停止,標記,掃動,恢復

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

  1. 完全暫停應用程序。
  2. 通過跟蹤對象圖(即,遞歸地遵循引用),標記所有可到達的對象(從根集開始,參見上文)。
  3. 釋放所有無法訪問的對象。
  4. 恢復應用程序。

在單線程環境中,這很容易想象:負責分配新對象的調用將立即返回新對象,或者,如果堆已滿,則啟動上述過程以釋放空間,然后執行通過完成分配并返回對象。
沒有一個JVM垃圾收集器像這樣工作。 但是,最好理解垃圾收集器的這種基本形式,因為可用的垃圾收集器實質上是上述過程的優化。 JVM不實現這種垃圾回收的兩個主要原因是:

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

壓縮與非壓縮垃圾回收

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

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

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

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

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

分代垃圾收集

大多數現實世界中的應用程序傾向于對短期對象(即已分配的對象,短暫使用的對象,然后不再引用)執行大量分配。 分代垃圾收集器嘗試利用此觀察結果以提高CPU效率(換句話說,具有更高的吞吐量 )。 (更正式地說,大多數應用程序具有此行為的假設被稱為弱代假設 。)

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

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

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

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

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

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

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

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

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

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

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

平行收集

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

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

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

增量收集

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

并發收集

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

可用的熱點垃圾收集器

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

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

在JDK 1.6和JDK 1.7的更高版本中,有一個新的垃圾收集器,稱為G1 (代表Garbage First )。 像CMS收集器一樣,其目的是嘗試減輕或消除長時間停頓世界停頓的需求,并且它的大部分工作都是在短暫的停頓世界漸進停頓的同時進行的,其中一些工作也在??完成中與應用程序同時進行。 與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,在沒有介紹的情況下,輸出對于分析而言更加不透明。 我希望在以后的更新中對此進行介紹。

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

結論

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

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


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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373518.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373518.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373518.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據結構之楊氏矩陣

轉自: http://blog.csdn.net/jiyanfeng1/article/details/8189228轉載于:https://www.cnblogs.com/neversayno/p/5256262.html

mysql 導出 沒有函數_沒有MYSQL FILE函數的CSV導出

構建最佳CSV。你可以按照以下方式做。$filename data.csv;$csv_terminated "\n";$csv_separator ",";$csv_enclosed ";$csv_escaped "\\";$results array(1,2,3);// value$schema_insert ;$header array(a,b,c);// headerfor ($i 0…

使用jdk壓縮war包

首先安裝jdk 壓縮 ..../jdk/bin/jar -cvf file.war file 解壓 ..../jdk/bin/jar -xvf file.war 轉載于:https://www.cnblogs.com/chongchong88/p/6049690.html

MongoDB查詢性能分析—— explain 操作返回結果詳解

MongoDB 提供 db.collection.explain(), cursort.explain() 及 explain 命令獲取查詢計劃及查詢計劃執行統計信息。 explain 結果將查詢計劃以階段樹的形式呈現。 每個階段將其結果(文檔或索引鍵)傳遞給父節點。 葉節點訪問集合或索引。 中間節點操縱由…

.deb包的安裝方法

deb是Debian linux的安裝格式,跟redhat的rpm非常相似,最基本的安裝命令是: dpkg -i file.deb dpkg是Debian Package的簡寫,是為Debian專門開發的管理系統套件,方便軟件的安裝,更新和移除。所有源自Debian的…

html回復評論_3天內看了3000多篇《哈佛商業評論》,挑出來最有用的分享下

上次分享過一個工具:一鍵批量下載公眾號歷史消息(后臺回復001獲取)。我把《哈佛商業評論》的歷史文章,全部爬了下來。該雜志被全球商業譽為“管理圣經”。我最感興趣的一部分是:個人管理。先搜索關鍵詞:&qu…

Java中的高性能庫

越來越多的庫被描述為高性能,并且有支持該要求的基準。 這是我所知道的選擇。 Disruptor庫 – http://code.google.com/p/disruptor/ LMAX旨在成為世界上最快的交易平臺。 顯然,為了實現這一目標,我們需要做一些特殊的事情,以通過…

Linux 命令行上執行多個命令(分隔符簡介使用)

Linux 系統可以在一個命令行上執行多個命令,相應的命令行的分隔符簡介及使用如下: ; 如果命令被分號(;)所分隔,那么命令會連續的執行下去,就算是錯誤的命令也會繼續執行后面的命令。示例如下: ls /home/; ls /etc/i…

codeforces 732/D 二分

給出考試時間和考試需要準備的時間&#xff0c;問最早考完所有科目的時間 二分答案 NlogN 二分抄神犇的寫法 感覺挺舒服的嘻嘻嘻 1 #include<bits/stdc.h>2 using namespace std;3 const int MAXN1e55;4 int N,M,d[MAXN],w[MAXN],cnt[MAXN];5 void read(int &x){6 …

XML基礎(二)

XML命名規則&#xff1a; ①名稱可以含字母、數字以及其他的字符 ②名稱不能以數字或標點符號開始 ③名稱不能以“xml”開始 ④名稱不能包含空格 ⑤盡量避免"-", "." ,":"等字符 xml元素是可擴展的。 XML屬性&#xff1a; 屬性提供有關元素的額外…

NoSQLBooster for MongoDB 中跨庫關聯查詢

? 使用 MongoDB 是我們常常會遇到一些特殊的需求需要跨庫關聯查詢&#xff0c;比如訂單明細缺商品重量需要補商品重量&#xff0c;而商品重量數據又在商品庫中&#xff0c;這事就需要跨庫關聯操作&#xff0c;示例代碼如下&#xff1a; // 使用 order 庫&#xff0c;注意語句…

網頁版的svn怎樣同步代碼_學會使用Hdlbits網頁版Verilog代碼仿真驗證平臺

大家推薦一款網頁版的 Verilog代碼編輯仿真驗證平臺&#xff0c;這個平臺是國外的一家開源FPGA學習網站&#xff0c;通過“https://hdlbits.01xz.net/wiki/Main_Page”地址鏈接進入網頁&#xff0c;在該網頁上可以進行Verilog代碼的編寫、綜合&#xff0c;而且最后還能夠仿真出…

遇到的零碎問題

Show()時&#xff0c;其他窗口仍可響應&#xff0c;ShowDialog()時其他窗口無響應。 在public Form1()中使用messagebox會先出現信息窗口&#xff0c;再顯示主窗體&#xff0c;故考慮加入了start按鈕。 play()會被打斷&#xff0c;PlaySync&#xff08;&#xff09;會播放完再執…

Tomcat上具有JAX-WS的Web服務

讓我們假設一家企業正在一個集中式系統中維護用戶身份驗證詳細信息。 我們需要創建一個AuthenticationService&#xff0c;它將獲取憑據&#xff0c;對其進行驗證并返回狀態。 其余的應用程序將使用AuthenticationService對用戶進行身份驗證。 創建AuthenticationService接口&a…

Python的下載及安裝

1、官網下載地址&#xff1a;https://www.python.org/downloads/ 2、python設置環境變量&#xff1a; 在系統變量里添加Python的安裝位置 3、在cmd里輸入python里即可轉載于:https://www.cnblogs.com/fun0623/p/5257573.html

MongoDB 字段拼接 $concat(aggregation)

$concat 拼接字符串操作&#xff0c;返回拼接后的字符串。語法格式如下&#xff1a; { $concat: [ <expression1>, <expression2>, ... ] }參數可以是任何有效的表達式&#xff0c;只要它們解析為字符串即可。 有關表達式的更多信息&#xff0c;請參閱表達式。 示…

cmake mysql 編譯參數_Cmake-MySQL編譯參數說明

Cmake-MySQL編譯參數說明(來源于MySQL官方手冊)https://dev.mysql.com/doc/refman/5.6/en/source-configuration-options.htmlFormats Description DefaultIntroduced Removed ##格式描述默認導入刪除BUILD_CONFIG Use same build options as official releases ##b使用相同…

動態給H5頁面綁定數據,基本萬能無錯誤!

此為原創&#xff0c;轉載請注明出處&#xff01; /* * 共通用綁定頁面數據用方法 * * param bingData 需要綁定的數據 * * return 無 * */function commonBindData(bingData) { // 取得需綁定的json數據 var jsonArray eval("(" bingData ")"); // …

c語言函數---M

書畫小說軟件 制作更滿意的讀、更舒心的寫、更輕松的公布最全古典小說網 由本軟件公布所得main()主函數 每一C 程序都必須有一main()函數, 能夠依據自己的愛好把它放在程序的某 個地方。有些程序猿把它放在最前面, 而還有一些程序猿把它放在最后面, 不管放 在哪個地方, 下面幾…

使用Apache ActiveMQ的JMS開發基礎

去年是我嘗試JMS的時候。 背后的想法和概念讓我有些困惑&#xff0c;但是當我知道它的用途后&#xff0c;我很快就掌握了它。 在本文中&#xff0c;我將展示使用Apache ActiveMQ作為后端使用Java開發簡單的生產者/消費者的基礎。 讓我們首先從概念開始&#xff0c;這是一個簡單…