Java垃圾收集蒸馏
串行,并行,并發(fā),CMS,G1,Young Gen,New Gen,Old Gen,Perm Gen,Eden,Tenured,Survivor Spaces,Safepoints和數(shù)百個JVM啟動標志。 在嘗試調(diào)優(yōu)垃圾收集器并嘗試從Java應用程序獲取所需的吞吐量和延遲時,這是否使您感到困惑? 如果確實如此,那就不用擔心,您并不孤單。 描述垃圾收集的文檔就像飛機的手冊頁。 每個旋鈕和轉盤都有詳細的說明,但是找不到任何關于如何飛行的指南。 本文將嘗試解釋為特定工作負載選擇和調(diào)整垃圾收集算法時的權衡取舍。
重點將放在Oracle Hotspot JVM和OpenJDK收集器上,因為它們是最常用的收集器。 最后,將討論其他商用JVM以說明替代方案。
權衡
明智的人們不斷告訴我們: “您一無所獲” 。 當我們得到一些東西時,我們通常不得不放棄一些回報。 當涉及到垃圾收集時,我們會使用3個主要變量來設置收集器的目標:
注意: Hotspot通常無法實現(xiàn)這些目標,并且會在沒有任何警告的情況下默默地繼續(xù)運行,因為它已經(jīng)大大偏離了目標。
延遲是事件之間的分布。 增加平均等待時間以減少最壞情況的等待時間或降低等待時間可能是可以接受的。 我們不應將“實時”一詞解釋為意味著最低的延遲。 實時是指無論吞吐量如何都具有確定性的延遲。
對于某些應用程序工作負載,吞吐量是最重要的目標。 一個示例將是長期運行的批處理作業(yè)。 只要可以更快地完成整個作業(yè),那么在進行垃圾收集時是否偶爾將批處理作業(yè)暫停幾秒鐘并不重要。
對于幾乎所有其他工作負載,從面向人類的交互式應用程序到金融交易系統(tǒng),如果系統(tǒng)在某些情況下無法響應的時間超過幾秒鐘甚至幾毫秒,則可能會帶來災難。 在金融交易中,通常值得犧牲一些吞吐量以換取一致的延遲。 我們可能還會有一些應用程序,這些應用程序受到可用物理內(nèi)存量的限制,并且必須保持占用空間,在這種情況下,我們必須放棄延遲和吞吐量方面的性能。
權衡通常表現(xiàn)如下:
- 通過為垃圾回收算法提供更多的內(nèi)存,可以在很大程度上減少作為攤銷成本的垃圾回收成本。
- 通過包含活動集并保持堆大小較小,可以減少由于垃圾收集而導致的觀察到的最壞情況的延遲引起的暫停。
- 通過管理堆和世代大小以及控制應用程序的對象分配速率,可以減少出現(xiàn)暫停的頻率。
- 通過與應用程序同時運行GC,可以減少較大的暫停頻率,有時會犧牲吞吐量。
對象壽命
垃圾收集算法通常經(jīng)過優(yōu)化,以期大多數(shù)對象的生存時間很短,而很少有對象生存時間很長。 在大多數(shù)應用程序中,生存期很長的對象往往構成隨時間分配的對象的很小一部分。 在垃圾收集理論中,這種觀察到的行為通常被稱為“ 嬰兒死亡率 ”或“ 弱代假設 ”。 例如,循環(huán)迭代器通常壽命很短,而靜態(tài)字符串實際上是永生的。
實驗表明,世代垃圾收集器通常可以比非世代收集器支持更大數(shù)量級的吞吐量,因此幾乎在服務器JVM中廣泛使用。 通過分離對象的世代,我們知道新分配對象的區(qū)域對于活動對象可能非常稀疏。 因此,收集器在此新區(qū)域中清除少量活動對象并將其復制到較舊對象的另一個區(qū)域中可以非常有效。 熱點垃圾收集器根據(jù)生存的GC周期數(shù)記錄對象的年齡。
注意:如果您的應用程序持續(xù)生成許多可以生存很長時間的對象,則可以預期您的應用程序將花費大量的時間進行垃圾回收,并希望花費大量的時間來調(diào)整Hotspot垃圾收集器。 這是由于世代“過濾器”效率降低時發(fā)生的GC效率降低,以及導致更頻繁地收集更長壽命的世代的成本。 老一輩人稀少,因此老一輩人收集算法的效率往往要低得多。 分代垃圾收集器通常以兩個不同的收集周期運行:收集短期對象的次要垃圾收集,收集較舊區(qū)域的次要垃圾收集。
世界停止活動
應用程序在垃圾回收期間遭受的暫停是由于所謂的世界停止事件造成的。 為了使垃圾收集器運行,出于實際工程上的原因,有必要定期停止正在運行的應用程序,以便可以管理內(nèi)存。 根據(jù)算法的不同,不同的收集器將在特定的執(zhí)行點停下世界,并持續(xù)不同的時間。 要使應用程序完全停止,必須暫停所有正在運行的線程。 垃圾收集器通過發(fā)信號通知線程在到達“ 安全點 ”時停止運行來做到這一點,這是程序執(zhí)行過程中所有GC根已知且所有堆對象內(nèi)容一致的點。 根據(jù)線程在做什么,可能需要一些時間才能達到安全點。 安全點檢查通常在方法返回和回送邊沿上執(zhí)行,但可以在某些地方進行優(yōu)化,從而使其在動態(tài)上更加罕見。 例如,如果線程正在復制大型數(shù)組,克隆大型對象或執(zhí)行具有有限界限的單調(diào)計數(shù)循環(huán),則到達安全點可能要花費幾毫秒的時間。 安全時間(TTS)是低延遲應用程序中的重要考慮因素。 通過啟用?XX:+ PrintGCApplicationStoppedTime標志以及其他GC標志,可以浮現(xiàn)此時間。
注意:對于具有大量正在運行的線程的應用程序,當世界停止事件發(fā)生時,隨著線程在釋放后恢復,系統(tǒng)將承受重大的調(diào)度壓力。 因此,較少依賴于世界停止事件的算法可能會更有效。
熱點堆組織
要了解不同收集器的工作方式,最好是探索如何組織Java堆來支持分代收集器。
伊甸園是最初分配大多數(shù)對象的區(qū)域。 幸存者空間是臨時存儲對象,這些對象在伊甸園空間中幸存下來。 討論次要收藏時,將描述幸存者空間的使用情況。 伊甸園和幸存者空間統(tǒng)稱為“年輕”或“新生代”。
壽命足夠長的對象最終將提升為使用期限 。
燙發(fā)生成是運行時將其“知道”為有效的對象(例如類和靜態(tài)字符串)存儲的地方。 不幸的是,在許多應用程序中持續(xù)使用類加載的常見用法使燙發(fā)生成背后的動機假設錯誤,即類是不朽的。 在Java 7中,已將字符串從permgen轉移到Tenured ,而從Java 8開始,不再存在perm生成,因此本文將不進行討論。 大多數(shù)其他商業(yè)收藏家并不使用單獨的燙發(fā)空間,而是傾向于將所有長期存在的物品視為永久使用。
注意:通過虛擬空間,收集器可以調(diào)整區(qū)域的大小,以滿足吞吐量和延遲目標。 收集器會保留每個收集階段的統(tǒng)計信息,并相應地調(diào)整區(qū)域大小,以達到目標。
對象分配
為了避免爭用,每個線程都分配有一個線程本地分配緩沖區(qū)(TLAB),從該線程中分配對象。 使用TLAB可以避免在單個內(nèi)存資源上發(fā)生爭用,從而使對象分配隨線程數(shù)擴展。 通過TLAB分配對象是非常便宜的操作; 它只是為對象大小增加一個指針,在大多數(shù)平臺上大約需要10條指令。 Java的堆內(nèi)存分配甚至比C運行時中的malloc便宜。
注意:盡管單個對象分配非常便宜,但必須進行次要收集的速率與對象分配速率成正比。
當TLAB耗盡時,一個線程只需向Eden空間請求一個新線程。 當伊甸園裝滿后,便開始小規(guī)模收集。
大對象(-XX:PretenureSizeThreshold = <n>)可能無法容納在年輕一代中,因此必須在舊一代中進行分配,例如大型數(shù)組。 如果將閾值設置為低于TLAB大小,則不會在舊版本中創(chuàng)建適合TLAB的對象。 新的G1收集器以不同的方式處理大型對象,稍后將在其單獨的部分中進行討論。
小型收藏
當伊甸園變滿時,將觸發(fā)次要回收。 這是通過將新一代的所有活動對象適當?shù)貜椭频叫掖嬲呖臻g或保有權空間來完成的。 復制到使用權空間稱為升級或使用權。 對于足夠舊的對象(– XX:MaxTenuringThreshold = <n>),或幸存者空間溢出時,將進行升級。
活動對象是應用程序可訪問的對象。 任何其他物體均無法到達,因此可以視為已死亡。 在次要集合中,首先通過遵循所謂的GC根目錄執(zhí)行活動對象的復制,然后反復復制可到達生存空間的任何對象。 GC根通常包括來自應用程序和JVM內(nèi)部靜態(tài)字段以及線程堆棧框架的引用,所有這些引用均有效指向應用程序的可訪問對象圖。
在世代集合中,新一代可訪問對象圖的GC根目錄還包括從舊一代到新一代的所有引用。 還必須對這些引用進行處理,以確保新一代中的所有可訪問對象在次要集合中都不會丟失。 通過使用“ 卡片表 ”來識別這些跨代參考。 熱點卡表是一個字節(jié)數(shù)組,其中每個字節(jié)用于跟蹤舊一代的相應512字節(jié)區(qū)域中跨代引用的潛在存在。 在將引用存儲到堆時,“存儲屏障”代碼將標記卡,以指示關聯(lián)的512字節(jié)堆區(qū)域中可能存在從舊一代到新一代的潛在引用。 在收集時,卡表用于掃描此類跨代引用,這些引用有效地代表了新一代的其他GC根。 因此,次要收藏的重大固定成本與上一代的大小成正比。
新一代Hotspot中有兩個幸存者空間,它們在“ 到太空 ”和“ 從太空 ”角色中交替出現(xiàn)。 在次要收集開始時,到太空幸存者空間始終是空的,并充當次要收集的目標復制區(qū)域。 先前的次要收藏的目標幸存者空間是起始空間的一部分,起始空間還包括伊甸園,在伊甸園中可以找到需要復制的活動對象。
少量GC收集的成本通常由將對象復制到幸存者和保有權空間的成本決定。 無法從次要收藏中幸存下來的對象可以有效地自由處理。 在次要收藏期間完成的工作與發(fā)現(xiàn)的活動對象的數(shù)量成正比,而不與新一代的大小成正比。 每次將伊甸園面積擴大一倍時,花在次要收藏上的總時間幾乎可以減少一半。 因此可以將內(nèi)存用于吞吐量。 將Eden大小增加一倍會導致每個收集周期的收集時間增加,但是如果要提升的對象數(shù)和舊一代的大小都恒定,則這相對較小。
注意:在熱點中,次要收藏是世界停止事件。 隨著越來越多的活動對象堆越來越大,這正Swift成為一個主要問題。 我們已經(jīng)開始看到需要同時收集年輕一代以達到暫停時間目標的需求。
主要收藏
主要藏品收集了老一代,以便可以從年輕一代中推廣物品。 在大多數(shù)應用程序中,絕大多數(shù)程序狀態(tài)最終出現(xiàn)在老一代。 對于前代來說,存在種類最多的GC算法。 有些會在填滿時壓縮整個空間,而另一些會與應用程序同時收集以防止填滿。
老一代的收藏家將嘗試預測何時需要收藏,以避免年輕一代的晉升失敗。 收集器跟蹤舊一代的填充閾值,并在超過該閾值時開始收集。 如果此閾值不足以滿足促銷要求,那么將觸發(fā)“ FullGC ”。 FullGC涉及推廣年輕一代的所有活物,然后收集和壓縮舊一代。 升級失敗是一項非常昂貴的操作,因為必須解開此循環(huán)中的狀態(tài)和升級對象,以便發(fā)生FullGC事件。
注意:為避免升級失敗,您將需要調(diào)整舊版本允許容納升級的填充(?XX:PromotedPadding = <n>)。
注意:當堆需要增長時,會觸發(fā)FullGC。 通過將–Xms和–Xmx設置為相同的值,可以避免這些堆大小調(diào)整的FullGC。
除FullGC之外,老一代的壓縮很可能是應用程序將遇到的最大的停頓停頓狀態(tài)。 壓縮的時間往往會隨著使用權空間中活動對象的數(shù)量線性增長。
有時,可以通過增加幸存者空間的大小和對象的年齡來降低占位空間的填充率,然后提升為占位空間。 但是,在促銷之前增加次要收藏中幸存者空間的大小和對象年齡(–XX:MaxTenuringThreshold = <n>)也會增加次要收藏物中的成本和暫停時間,這是由于未成年收藏者之間的生存空間之間的復制成本增加了集合。
串行收集器
串行收集器(-XX:+ UseSerialGC)是最簡單的收集器,是單處理器系統(tǒng)的不錯選擇。 它還具有所有收集器中最小的占地面積。 它對次要和主要集合都使用一個線程。 使用簡單的凹凸指針算法在持久空間中分配對象。 當使用權空間已滿時,將觸發(fā)主要集合。
并聯(lián)收集器
并行收集器有兩種形式。 并行收集器 (?XX:+ UseParallelGC),它使用多個線程來執(zhí)行年輕代的次要收集,并使用單個線程來執(zhí)行舊代的主要收集。 自Java 7u4起默認的Parallel Old收集器 (?XX:+ UseParallelOldGC)使用多個線程進行次要收集,并使用多個線程進行主要收集。 使用簡單的凹凸指針算法在持久空間中分配對象。 當使用權空間已滿時,將觸發(fā)主要集合。
在多處理器系統(tǒng)上,并行舊收集器將提供所有收集器中最大的吞吐量。 直到發(fā)生收集為止,它對正在運行的應用程序沒有影響,然后將使用最有效的算法使用多個線程并行收集。 這使得Parallel Old Collector非常適合批處理應用。
收集舊版本的成本受要保留的對象數(shù)量的影響大于受堆大小影響的對象。 因此,可以通過提供更多內(nèi)存并接受更大但更少的收集暫停來提高Parallel Old收集器的效率,以實現(xiàn)更大的吞吐量。
期望使用此收集器獲得最快的次要收集,因為升級到保有空間僅是指針和復制操作的簡單顛簸。
對于服務器應用程序,Parallel Old收集器應該是第一個調(diào)用端口。 但是,如果主要的收集暫停時間超出了您的應用程序所能承受的范圍,則您需要考慮使用并發(fā)收集器,該并發(fā)收集器在應用程序運行時并發(fā)收集長期對象。
注意:在壓縮舊版本的同時,現(xiàn)代硬件上每GB實時數(shù)據(jù)的暫停時間大約為1-5秒。
注意:通過為CPU套接字本地的線程分配Eden內(nèi)存,并行收集器有時可以在多路CPU服務器應用程序上從-XX:+ UseNUMA獲得性能優(yōu)勢。 可惜的是,該功能對其他收集器不可用。
并發(fā)標記掃描(CMS)收集器
CMS(-XX:+ UseConcMarkSweepGC)收集器在舊版本中運行,以收集在大型收集期間不再可訪問的終身對象。 它與應用程序同時運行,目的是在老一代中保留足夠的可用空間,從而不會發(fā)生年輕一代的升級失敗。
升級失敗將觸發(fā)FullGC。 CMS遵循多個步驟:
當租用對象變得不可訪問時,CMS將回收該空間并將其放入空閑列表。 進行促銷時,必須在自由列表中搜索適合促銷對象尺寸的Kong。 與Parallel Collector相比,這增加了推廣成本,從而增加了Minor收藏的成本。
注意 :CMS不是壓縮收集器,隨著時間的推移,它可能導致舊的碎片化。 對象升級可能會失敗,因為大型對象可能不適合舊版本中的可用Kong。 發(fā)生這種情況時,會記錄“ 升級失敗 ”消息,并觸發(fā)FullGC壓縮活動的使用權對象。 對于此類壓縮驅動的FullGC,由于CMS僅使用單個線程進行壓縮,因此期望的延遲比使用Parallel Old收集器的主要收集更糟糕。
CMS通常與應用程序并發(fā),這具有許多含義。 首先,CPU時間由收集器占用,從而減少了可用于應用程序的CPU。 CMS所需的時間與將對象提升到保有空間的數(shù)量成線性增長。 其次,對于并發(fā)GC周期的某些階段,必須將所有應用程序線程帶入一個安全點,以標記GC根并執(zhí)行并行重新標記以檢查變異。
注意 :如果應用程序發(fā)現(xiàn)使用權對象發(fā)生了重大變化,則重新標記階段可能很重要,在極端情況下,重新標記階段可能比使用Parallel Old Collector進行完全壓縮要花費更長的時間。
CMS使FullGC成為不太頻繁的事件,但代價是吞吐量降低,更昂貴的次要收集和更大的占用空間。 與并行收集器相比,吞吐量的下降幅度可能在10%-40%之間,具體取決于提升率。 CMS還需要占用20%的空間,以容納其他數(shù)據(jù)結構和“浮動垃圾”,這些并發(fā)標記在傳遞到下一個周期的并發(fā)標記期間可能會丟失。
有時可以通過增加年輕一代空間和老一代空間的大小來降低高晉升率和由此產(chǎn)生的碎片。
注意 :如果CMS收集速度不足以跟上升級的速度,則CMS可能會遇到“ 并發(fā)模式故障 ”,這可以在日志中看到。 當收集開始得太晚時可能導致這種情況,有時可以通過調(diào)整來解決。 但是,當收集率無法跟上某些應用程序的高推廣率或高對象突變率時,也會發(fā)生這種情況。 如果應用程序的提升率或變異率太高,則您的應用程序可能需要進行一些更改以減輕提升壓力。 向這樣的系統(tǒng)添加更多的內(nèi)存有時會使情況變得更糟,因為CMS將需要更多的內(nèi)存來進行掃描。
垃圾優(yōu)先(G1)收集器
G1(-XX:+ UseG1GC)是Java 6中引入的新收集器,現(xiàn)已從Java 7u4開始正式支持。 這是一種部分并發(fā)的收集算法,該算法還嘗試在較小的增量“停止世界”停頓中壓縮占位空間,以盡量減少由于碎片而困擾CMS的FullGC事件。 G1是一個世代收集器,通過將其劃分為大量(?2000個)可變目的的固定大小區(qū)域(而不是出于相同目的的連續(xù)區(qū)域)來與其他收集器進行不同的組織。
G1采用同時標記區(qū)域的方法來跟蹤區(qū)域之間的引用,并將集合集中在具有最大可用空間的區(qū)域上。 然后,通過將活動對象疏散到一個空的區(qū)域,以停下來的停頓增量收集這些區(qū)域,從而在此過程中進行壓縮。 一個循環(huán)中要收集的區(qū)域稱為收集集 。
大于某個區(qū)域50%的對象分配在多個區(qū)域中的大型區(qū)域中。 在G1下,大型對象的分配和收集可能會非常昂貴,并且迄今為止幾乎沒有或沒有進行任何優(yōu)化。
任何壓縮收集器的挑戰(zhàn)不是對象的移動,而是對那些對象的引用的更新。 如果從許多區(qū)域引用了一個對象,則更新這些引用所花費的時間可能比移動該對象要長得多。 G1通過“ 記住的集合 ”跟蹤區(qū)域中哪些對象具有其他區(qū)域的引用。 記住集是標記為突變的牌的集合。 如果“記住的集合”變大,則G1會顯著降低速度。 當將對象從一個區(qū)域撤離到另一個區(qū)域時,相關的世界停止事件的長度往往與具有需要掃描和潛在修補的參考的區(qū)域數(shù)量成比例。
維護“記住的集”會增加次要收集的成本,從而導致暫停時間要比“平行老”或CMS的次要收集器更長。
G1是目標驅動程序,其時延為–XX:MaxGCPauseMillis = <n>,默認值= 200ms。 該目標將盡力而為地影響每個周期的工作量。 在幾十毫秒內(nèi)設置目標通常是徒勞的,而在撰寫本文時,針對數(shù)十毫秒的目標還不是G1的重點。
對于較大的堆,G1是一個很好的通用收集器,當應用程序可以容忍0.5-1.0秒范圍內(nèi)的增量壓縮暫停時,G1往往會變得碎片化。 G1傾向于減少CMS看到的最壞情況的停頓頻率,這是因為碎片化的代價是擴展了次要收藏集并增加了舊一代的壓縮能力。 大多數(shù)暫停最終都局限于區(qū)域壓縮,而不是全部堆壓縮。
像CMS一樣,G1也可能無法跟上晉升率,并且會退回到世界末日的FullGC。 就像CMS具有“ 并發(fā)模式故障 ”一樣,G1可能會發(fā)生疏散故障,在日志中被視為“ 空間溢出 ”。 當沒有空閑區(qū)域可將對象撤離時,就會發(fā)生這種情況,這類似于升級失敗。 如果發(fā)生這種情況,請嘗試使用更大的堆和更多的標記線程,但是在某些情況下,可能需要更改應用程序以降低分配率。
對于G1來說,一個具有挑戰(zhàn)性的問題是處理受歡迎的物體和區(qū)域。 當區(qū)域中的活動對象未從其他區(qū)域大量引用時,逐步停止壓縮將非常有效。 如果某個對象或區(qū)域很受歡迎,則記住的集合會很大,G1將嘗試避免收集這些對象。 最終,它別無選擇,這會導致在堆壓縮時非常頻繁的中長度暫停。
替代并行收集器
CMS和G1通常被稱為并發(fā)收集器。 當您查看所執(zhí)行的全部工作時,很顯然,年輕一代,晉升甚至許多老一代工作根本不是同時進行的。 CMS在大多數(shù)情況下是并發(fā)的。 G1更像是一個停滯不前的增量收集器。 CMS和G1都具有重大且定期發(fā)生的世界停止事件,以及最壞的情況,通常使它們不適合嚴格的低延遲應用程序,例如金融交易或反應性用戶界面。
可以使用其他收集器,例如Oracle JRockit Real Time,IBM Websphere Real Time和Azul Zing。 JRockit和Websphere收集器在大多數(shù)情況下都比CMS和G1具有延遲優(yōu)勢,但是經(jīng)常遇到吞吐量限制,并且仍然遭受重大的世界末日事件。 Zing是該作者所知的唯一Java收集器,它可以真正地并發(fā)進行收集和壓縮,同時保持所有代的高吞吐率。 Zing確實有一些毫秒級的世界停止事件,但這些事件是與收集周期中的相移有關的,這些相移與活動對象集的大小無關。
對于所包含的堆大小,高分配速率時,JRockit RT可以實現(xiàn)數(shù)十毫秒的典型暫停時間,但有時必須恢復到完全壓縮暫停。 Websphere RT可以通過受限制的分配速率和活動集大小來實現(xiàn)單位毫秒的暫停時間。 通過在所有階段(包括次要收集期間)并發(fā)執(zhí)行,Zing可以以高分配率實現(xiàn)亞毫秒級的暫停。 無論堆大小如何,Zing都能夠保持這種一致的行為,從而使用戶可以根據(jù)需要應用大堆大小,從而滿足應用程序吞吐量或對象模型狀態(tài)需求,而不必擔心增加暫停時間。
對于所有針對延遲的并發(fā)收集器,您必須放棄一些吞吐量并增加占用空間。 根據(jù)并發(fā)收集器的效率,您可能會放棄一點吞吐量,但始終會增加大量占用空間。 如果是真正的并發(fā),幾乎沒有停滯事件,則需要更多的CPU內(nèi)核來啟用并發(fā)操作并保持吞吐量。
注意:分配足夠的空間后,所有并發(fā)收集器往往會更有效地發(fā)揮作用。 作為一個經(jīng)驗法則,您應該將堆的預算至少為活動集大小的2到3倍,以進行有效的操作。 但是,用于維持并發(fā)操作的空間需求隨著應用程序吞吐量以及相關的分配和提升速率而增長。 因此,對于更高吞吐量的應用程序,可以保證更高的堆大小與活動集比率。 鑒于當今系統(tǒng)可用的巨大內(nèi)存空間,在服務器端很少出現(xiàn)問題。
垃圾收集監(jiān)控和調(diào)整
要了解您的應用程序和垃圾收集器的行為方式,請至少使用以下設置啟動JVM:
-verbose:gc -Xloggc: -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime然后將日志加載到Chewiebug之類的工具中進行分析。
要查看GC的動態(tài)性質(zhì),請啟動JVisualVM并安裝Visual GC插件。 這將使您能夠如下所示查看適用于您的應用程序的GC。
為了了解您的應用程序的GC需求,您需要可以重復執(zhí)行的代表性負載測試。 當您掌握每個收集器的工作方式時,然后以不同的配置運行負載測試作為實驗,直到達到吞吐量和延遲目標。 從最終用戶的角度衡量延遲很重要。 這可以通過在直方圖中捕獲每個測試請求的響應時間來實現(xiàn),您可以在此處了解更多信息。 如果您的延遲峰值超出可接受范圍,請嘗試將其與GC日志關聯(lián),以確定是否是GC問題。 其他問題可能會導致延遲峰值。 要考慮的另一個有用工具是jHiccup ,它可用于跟蹤JVM中以及整個系統(tǒng)中的暫停。 用jHiccup測量您的空閑系統(tǒng)幾個小時,您通常會感到非常驚訝。
如果延遲高峰是由于GC引起的,則投資調(diào)整CMS或G1以查看您的延遲目標是否可以實現(xiàn)。 有時由于高分配和提升率以及低延遲要求,這可能無法實現(xiàn)。 GC調(diào)整可以成為一項高技能的練習,通常需要更改應用程序以減少對象分配率或對象壽命。 如果是這種情況,則可能需要在時間和在GC調(diào)整和應用程序更改上花費的資源之間進行商業(yè)平衡,例如,可能需要購買商業(yè)并發(fā)壓縮JVM中的一種,例如JRockit Real Time或Azul Zing。
翻譯自: https://www.javacodegeeks.com/2013/07/java-garbage-collection-distilled.html
總結
以上是生活随笔為你收集整理的Java垃圾收集蒸馏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 送礼六大禁忌 送礼六大禁忌是什么
- 下一篇: Spring Data Solr教程:排