中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么理解JVM1.4.1版本中的JVM垃圾收集策略

發布時間:2021-10-23 16:57:01 來源:億速云 閱讀:106 作者:柒染 欄目:編程語言

本篇文章給大家分享的是有關怎么理解JVM1.4.1版本中的JVM垃圾收集策略,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

我們分析了引用計數、復制、標記-清除和標記-整理這些經典的JVM垃圾收集技術。其中每一種方法在特定條件下都有其優點和缺點。JVM1.2及以后版本使用的技術稱為分代JVM垃圾收集(generationalgarbagecollection),它結合了這兩種技術以結合二者的長處,結果就是對象分配開銷非常小。

JVM1.4.1中的垃圾收集

我們分析了引用計數、復制、標記-清除和標記-整理這些經典的JVM垃圾收集技術。其中每一種方法在特定條件下都有其優點和缺點。例如,當有很多對象成為垃圾時,復制可以做得很好,但是有許多長壽對象時它就變得很糟(要反復復制它們)。相反,標記-整理對于長壽對象可以做得很好(只復制一次),但是當有許多短壽對象時就沒有那么好了。JVM1.2及以后版本使用的技術稱為分代JVM垃圾收集(generationalgarbagecollection),它結合了這兩種技術以結合二者的長處,結果就是對象分配開銷非常小。

老對象和年輕對象

在任何一個應用程序堆中,一些對象在創建后很快就成為垃圾,另一些則在程序的整個運行期間一直保持生存。經驗分析表明,對于大多數面向對象的語言,包括Java語言,絕大多數對象――可以多達98%(這取決于您對年輕對象的衡量標準)是在年輕的時候死亡的。可以用時鐘秒數、對象分配以后?h內存管理子系統分配的總字節或者對象分配后經歷的JVM垃圾收集的次數來計算對象的壽命。但是不管您如何計量,分析表明了同一件事――大多數對象是在年輕的時候死亡的。大多數對象在年輕時死亡這一事實對于收集器的選擇很有意義。特別是,當大多數對象在年輕時死亡時,復制收集器可以執行得相當好,因為復制收集器完全不訪問死亡的對象,它們只是將活的對象復制到另一個堆區域中,然后一次性收回所有的剩余空間。

那些經歷過***次JVM垃圾收集后仍能生存的對象,很大部分會成為長壽的或者***的對象。根據短壽對象和長壽對象的混合比例,不同JVM垃圾收集策略的性能會有非常大的差別。當大多數對象在年輕時死亡時,復制收集器可以工作得很好,因為年輕時死亡的對象永遠不需要復制。不過,復制收集器處理長壽對象卻很糟糕,它要從一個半空間向另一個半空間反復來回復制這些對象。相反,標記-整理收集器對于長壽對象可以工作得很好,因為長壽對象趨向于沉在堆的底部,從而不用再復制。不過,標記-清除和標記-理整收集器要做很多額外的分析死亡對象的工作,因為在清除階段它們必須分析堆中的每一個對象。

分代收集

分代收集器(generializationalcollector)將堆分為多個代。在年輕的代中創建對象,滿足某些提升標準的對象,如經歷了特定次數JVM垃圾收集的對象,將被提升到下一更老的代。分代收集器對不同的代可以自由使用不同的收集策略,對各代分別進行JVM垃圾收集。

小的收集

分代收集的一個優點是它不同時收集所有的代,因此可以使JVM垃圾收集暫停更短。當分配器不能滿足分配請求時,它首先觸發一個小的收集(minorcollection),它只收集最年輕的代。因為年輕代中的許多對象已經死亡,復制收集器完全不用分析死亡的對象,所以小的收集的暫停可以相當短并通常可以回收大量的堆空間。如果小的收集釋放了足夠的堆空間,那么用戶程序就可以立即恢復。如果它不能釋放足夠的堆空間,那么它就繼續收集上一代,直到回收了足夠的內存。(在JVM垃圾收集器進行了全部收集以后仍不能回收足夠的內存時,它將擴展堆或者拋出OutOfMemoryError)。

代間引用

跟蹤JVM垃圾收集器,如復制、標記-清除和標記-整理等JVM垃圾收集器,都是從根集(rootset)開始掃描,遍歷對象間的引用,直到訪問了所有活的對象。

分代跟蹤收集器從根集開始,但是并不遍歷指向更老一代中對象的引用,這減少了要跟蹤的對象圖的大小。但是這也帶來一個問題――如果更老一代中的對象引用一個不能通過從根開始的所有其他引用鏈到達的更年輕的對象該怎么辦?

為了解決這個問題,分代收集器必須顯式地跟蹤從老對象到年輕對象的引用并將這些老到年輕的引用加入到小的收集的根集中。有兩種創建從老對象到年輕對象的引用的方法。要么是將老對象中包含的引用修改為指向年輕對象,要么是將引用其他年輕對象的年輕對象提升為更老的一代。

跟蹤代間引用

不管一個老到年輕的引用是通過提升還是指針修改創建的,JVM垃圾收集器在進行小的收集時需要有全部老到年輕的引用。做到這一點的一種方法是跟蹤老的代,但是這顯然有很大的開銷。更好的一種方法是線性掃描老的代以查找對年輕對象的引用。這種方法比跟蹤更快并有更好的區域性(locality),但是仍然有很大的工作量。

賦值函數(mutator)和JVM垃圾收集器可以共同工作以在創建老到年輕的引用時維護它們的完整列表。當對象提升為更老一代時,JVM垃圾收集器可以記錄所有由于這種提升而創建的老到年輕的引用,這樣就只需要跟蹤由指針修改所創建的代間引用。

JVM垃圾收集器可以有幾種方法跟蹤由于修改現有對象中的引用而產生的老到年輕的引用。它可以使用在引用計數收集器中維護引用計數的同樣方法(編譯器可以生成圍繞指針賦值的附加指令)跟蹤它們,也可以在老一代堆上使用虛擬內存保護以捕獲向老對象的寫入。另一種可能更有效的虛擬內存方法是在老一代堆中使用頁修改臟位(pagemodificationdirtybit),以確定為找到包含老到年輕指針的對象時要掃描的塊。

用一點小技巧,就可以避免跟蹤每一個指針修改并檢查它是否跨越代邊界的開銷。例如,不需要跟蹤針對本地或者靜態變量的存儲,因為它們已經是根集的一部分了。也可以避免跟蹤存儲在某些構造函數中的指針,這些構造函數只用于初始化新建對象的字段(即所謂初始化存儲(initializingstores)),因為(幾乎)所有對象都是分配到年輕代中。不管是什么情況,運行庫都必須維護一個老對象到年輕對象的引用集并在收集年輕代時將這些引用添加到根集中。

在圖1中,箭頭表示堆中對象間的引用。紅色箭頭表示必須添加到根集中供小的收集使用的老到年輕的引用。藍色箭頭表示從根集或者年輕代到老對象的引用,在只收集年輕代時不需要跟蹤它們。

怎么理解JVM1.4.1版本中的JVM垃圾收集策略

卡片標記

SunJDK使用一種稱為卡片標記(cardmarking)算法的改進算法以標識對老一代對象的字段中包含的指針的修改。在這種方法中,堆分為一組卡片,每個卡片一般都小于一個內存頁。JVM維護著一個卡片映射,對應于堆中的每一個卡片都有一個位(在某些實現中是一個字節)。每次修改堆中對象中的指針字段時,就在卡片映射中設置對應那張卡片的相應位。在JVM垃圾收集時,就對與老一代中卡片相關聯的標記位進行檢查,對臟的卡片掃描以尋找對年輕代有引用的對象。然后清除標記位。卡片標記有幾項開銷――卡片映射所需的額外空間、對每一個指針存儲所做的額外工作,以及在JVM垃圾收集時做的額外工作。對每一個非初始化堆指針存儲,卡片標記算法可以只增加兩到三個機器指令,并要求在小的收集時對所有臟卡片上的對象進行掃描。

JDK1.4.1默認收集器

在默認情況下,JDK1.4.1將堆分為兩部分,一個年輕的代和一個老的代(實際上,還有第三部分――***空間,它用于存儲裝載的類和方法對象)。借助于復制收集器,年輕的代又分為一個創建空間(通常稱為Eden)和兩個生存半空間。

老的代使用標記-整理收集器。對象在經歷了幾次復制后提升到老的代。小的收集將活的對象從Eden和一個生存半空間復制到另一個生存半空間,并可能提升一些對象到老的代。大的收集(majorcollection)既會收集年輕的代,也會收集老的代。System.gc()方法總是觸發一個大的收集,這就是應該盡量少用(如果不能完全不用的話)System.gc()的原因之一,因為大的收集要比小的收集花費長得多的時間。沒有辦法以編程方式觸發小的收集。

其他收集選項

除了默認情況下使用的復制收集器和標記-整理收集器,JDK1.4.1還包含其他四種JVM垃圾收集算法,每一種適用于不同的目的。JDK1.4.1包含一個增量收集器(自JDK1.2就已經出現了)和三種在多處理器系統中進行更有效收集的新收集器――并行復制收集器、并行清除(scavenging)收集器和并發標記-清除收集器。這些新收集器是為了解決在多處理器系統中JVM垃圾收集器成為伸縮性瓶頸這一問題的。圖2顯示了在什么時候選擇備用收集選項的指導。

怎么理解JVM1.4.1版本中的JVM垃圾收集策略

增量收集

增量收集選項自1.2起就成為JDK的一部分。增量收集減少了JVM垃圾收集暫停,以犧牲吞吐能力為代價,這使它只在更短的收集暫停非常重要時才值得考慮,如接近實時的系統。

Train算法是JDK用于增量收集的算法,它在堆中老的代和年輕的代之間創建一個新區域。這些堆區域劃分為“火車(train)”,每個火車又分為一系列的“車廂(car)”。每個車廂可以分別收集。結果,每個火車車廂組成單獨的一代,這意味著不但要跟蹤老到年輕的引用,而且還要跟蹤從老的火車到年輕的火車以及老的車廂到年輕的車廂的引用。這為賦值函數(mutator)和JVM垃圾收集器帶來了大量的額外工作,但是可以得到更短的收集暫停。

并行收集器和并發收集器

JDK1.4.1中新的收集器都是為解決多處理器系統中JVM垃圾收集器的問題而設計的。因為大多數JVM垃圾收集算法會在一段時間里使系統停止,單線程的收集器很快會成為伸縮性瓶頸,因為在JVM垃圾收集器將用戶程序線程掛起時,除了一個處理器之外,其他的處理器都是空閑的。新收集器中的兩個――并行復制收集器和并發標記-清除收集器――設計為減少收集暫停時間。另一個是并行清除收集器,它是為在大堆上的更高吞吐能力而設計的。

并行復制收集器用JVM選項-XX:+UseParNewGC啟用,是一個年輕代復制收集器,它將JVM垃圾收集的工作分為與CPU數量一樣多的線程。并發標記-清除收集器由-XX:+UseConcMarkSweepGC選項啟用,它是一個老代標記-清除收集器,它在初始標記階段(及在以后暫短重新標記階段)暫短地停止整個系統,然后恢復用戶程序,同時JVM垃圾收集器線程與用戶程序并發地執行。并行復制收集器和并發標記-清除收集器基本上是默認的復制收集器和標記-整理收集器的并發版本。由-XX:+UseParallelGC啟用的并行清除收集器是年輕代收集器,針對多處理器系統上非常大(吉字節以及更大的)堆進行了優化。

選擇一種算法

有六種算法可以選擇,您可能不知道要使用哪一種。圖2提供了一些指導,將收集器分為單線程和并發的,以及分為短暫停和高吞吐能力的。只要您掌握了應用程序和部署環境的信息,就足以選擇合適的算法。對于許多應用程序,默認的收集器可以工作得很好――因此如果您沒有性能問題,那么就沒必要加入更多的復雜性。不過,如果您的應用程序是部署在多處理器系統上或者使用非常大的堆,那么改變收集器選項可能會有巨大的性能提升。

微調JVM垃圾收集器

JDK1.4.1還包括大量的微調JVM垃圾收集的選項。調整這些選項并衡量它們的效果可能會花費您大量時間,因此在試圖微調JVM垃圾收集器之前先對您的應用程序進行徹底的配置(profile)和優化,這樣您的微調工作可能會得到更好的結果。

微調JVM垃圾收集首先要做的是檢查冗長的GC輸出。這會使您得到JVM垃圾收集操作的頻率、定時和持續時間等信息。最簡單的JVM垃圾收集微調就是擴大***堆的大小(-Xmx)。隨著堆的增大,復制收集會變得更有效,所以在增大堆時,您就減少了每個對象的收集成本。除了增加***堆的大小,還可以用選項-XX:NewRatio增加分配給年輕代的空間份額。也可以用-Xmn選項顯式指定年輕代的大小。

隨著JVM的發展,默認JVM垃圾收集器變得越來越好了。JDK1.2及以后版本所使用的分代JVM垃圾收集器提供了比早期JDK所使用的標記-清除-整理收集器好得多的分配和收集性能。JDK1.4.1通過增加新的針對多處理器系統和非常大的堆的多線程收集選項,進一步改進了JVM垃圾收集的效率。

以上就是怎么理解JVM1.4.1版本中的JVM垃圾收集策略,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永安市| 平陆县| 新竹市| 南乐县| 澎湖县| 金塔县| 福海县| 固原市| 民县| 吉林市| 鄯善县| 洪雅县| 宝鸡市| 陵川县| 马山县| 江源县| 当阳市| 连平县| 阳泉市| 琼海市| 香港| 德保县| 杭锦旗| 金门县| 射阳县| 哈尔滨市| 玉屏| 外汇| 重庆市| 澄江县| 叶城县| 镇沅| 余干县| 南岸区| 九江县| 丹东市| 固安县| 弥渡县| 昌乐县| 彭阳县| 宕昌县|