成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java SE 6 Hotspot虛擬機垃圾回收調優

開發 后端
譯者按:有些術語實在很別扭,不過譯者文采有限,沒法找到合適的中文詞匯,不太影響理解,湊合看吧。

譯者按:有些術語實在很別扭,不過譯者文采有限,沒法找到合適的中文詞匯,不太影響理解,湊合看吧。

1. 概述

Java 平臺標準版(Java SE™)被廣泛應用于各種應用,從桌面上的小小的 applet 到大型服務器上的 Web Service 無處不在。為了支持各種不同的部署場景,Java HotSpot™ 虛擬機提供了多種垃圾回收器,每種都為滿足不同的需求而設定。這是也為了滿足大大小小不同應用需求的一部分。不過,那些需要高性能應用的用戶、開發者和管理員們也被選擇適合他們應用的恰當的垃圾回收器的繁瑣困擾著。取消這些額外操作的重要一步是在 J2SE™ 5.0 中作出的:垃圾回收器會根據應用運行的計算機類型而作出選擇。

這個垃圾回收器的“更好的選擇”總的說是一種進步,不過,這并不意味著對所有的應用這都是最好的選擇。對于有極端的性能或其他需求的用戶,仍需要顯式地指定垃圾回收器,并調優某些參數,以達到滿意的性能。本文就為這些需求提供了一些相關信息。首先,本文會基于串行的 stop-the-world 垃圾回收器來介紹垃圾回收器的一般性特征和基本調優開關。接下來會介紹其他垃圾回收器的特點和如何選擇一個垃圾回收器。

何時選擇垃圾回收器?對于一些應用,這個答案可能是“永遠不”。也就是說,在有低頻率、短時的垃圾收集器造成的停頓的情況下,大部分程序都運行良好。不過,這并不適用于很多程序,特別是那些處理大量數據(若干GB)、很多線程和需要處理很多事務的情況。

Amdahl 觀察到,大部分工作負載并不能被很好的并行化;有部分情況下總是會被順序執行,無法從并行化中獲益。這對 Java™ 平臺也是如此。特別的,在 J2SE 1.4 以前,Sun Java 平臺的虛擬機并不支持并行垃圾回收,這樣,在多處理器系統中,垃圾回收會對并行應用產生嚴重影響。

下圖顯示了一個除了垃圾回收以外均為完美可伸縮的理想系統的性能曲線。紅色曲線是一個在但處理器系統中會花費 1% 的時間在垃圾回收上的程序。它在 32 處理器的系統中,將損失 20% 的吞吐量。而一個花費 10% 時間在垃圾回收上的應用(不考慮單處理器系統中額外的垃圾回收時間)在系統擴張到 32 處理器系統中時,會損失超過 75% 的吞吐量 。

這意味著在小型開發系統中微不足道的速度問題當擴張到大規模系統中就可能成為嚴重的性能瓶頸。從另一個角度看,減少這樣的性能瓶頸的小改動就可以獲得很大的性能收益。對足夠大規模的系統,選擇合適的垃圾收集器并進行必要調優是絕對值得的。

對于大多數“小”應用(在現代處理器上大約需要100MB堆內存的應用)來說通常是足夠的。其他垃圾收集器會帶來額外的負載或復雜性,這回讓系統的某些行為付出一定的代價。如果一個應用不需要一個垃圾收集器的某個功能。那么就使用串行的垃圾收集器好了。一個不應該使用串行垃圾收集器場景是一個超多線程的大程序運行在一個大型的、有大量內存和兩個或多個處理器的系統中。當應用運行在這些服務器級的計算機上的時候,并行垃圾收集器會被缺省選擇(參見下面的功效學 )。

本文以 Solaris™ 操作系統(SPARC(R) 平臺版本)中的 Java SE 6 作為參考。不過,文中所述的概念和建議適用于所有支持的平臺,包括 Linux, Microsoft Windows 和 Solaris 操作系統(x86 平臺版本)。此外,文中的命令行參數也對所有平臺有效,雖然它們的缺省值在各個平臺可能有所不同。

2. 功效學(Ergonomics)

“功效學”是一個 J2SE 5.0 引入的概念。引入功效學概念是為了通過不設置或設置很少的幾個命令行參數的情況下提供更好的性能,這些參數包括:

  • 垃圾收集器,
  • 堆尺寸,
  • 和運行時編譯器

這里的參數選擇假定應用所運行的主機類型和應用的類型一致(也就是說,大型應用運行在大型的機器上)。這些選項簡化了垃圾回收的調優。選擇并行垃圾回收器,用戶可以指定應用的最大中斷時間和希望的吞吐量。這和指定堆大小來調優性能是相對應的。最常用的功效學相關的內容在可以參考 “Ergonomics in the 5.0 Java Virtual Machine” 這篇文章。建議在嘗試本文提到的細節配置之前嘗試該文章中介紹的功效學手段。

本文中的功效學特性被作為并行垃圾回收器的自適應尺寸策略的一部分。這包括指定垃圾回收性能的目標和性能調優的一些附加選項。

3.代

J2SE 平臺的優勢之一是它將內存分配、垃圾回收這些繁復的細節屏蔽了起來。然而,一旦垃圾回收成為主要的瓶頸,那么理解一下這些隱藏在背后的細節就變得有必要了。垃圾回收器對應用程序對對象的使用方式進行判斷,這個判斷會反映在可調優參數中,他們可以被調整,以提高性能而不犧牲掉抽象性。

當一個對象不再可能被從其他任何地方訪問到的時候就會被認為是垃圾了。最直接的垃圾回收算法就是簡單地迭代所有可找到的對象。任何沒有被跌帶到的對象都可以被認為是垃圾了。這個方法的用時和活著的對象數量成正比,這對于那些維護著大量活數據的程序來說是不可接受的。

從 J2SE 1.2 開始,虛擬機就引入了各種不同的垃圾回收算法,這些算法都使用分代垃圾收集。盡管原生的垃圾回收會檢查堆中的所有活著的對象,分代垃圾收集采用了很多觀測到的大部分應用程序的經驗特征,用來最小化發現廢棄的對象的工作量。最重要的經驗特征是 weak generational 假設,該假設認為大部分對象都只存活一少段時間。

下圖中的藍色區域是對象生存期的典型分布。橫軸是對象被分配后的生存期??v軸方向計算的字節數是相應生存期的對象的總字節數。左側的尖峰表明,對象在分配之后不久就被廢棄了。比如,迭代器對象常常只會在一個循環中被用到。

當然,有些對象確實活得要長一些,于是,分布曲線延伸到了右邊。比如,典型情況下,有些對象在初始化的時候被創建,并一直存活到進程結束。在這兩種極限情況之間,那些對象活的時間也是中等的,在圖中表現初來的就是從開始的峰值泄漏初來的藍色區域。有些應用可能會有看起來十分不同的分布曲線,不過絕大多數的進程都是這個常見的形狀。大部分對象都會“英年早逝”這個事實讓高效的垃圾收集變得具有可能性了。

為了為這樣的應用環境優化,內存被按照“代” (generation)進行管理,或者說,內存池中存放不同年齡的對象。當一個年齡斷被填滿后,就對該代的垃圾進行回收。在內存池中的大部分對象都是年輕的對象(年輕的代),而大部分對象也會在年輕的時候就成為垃圾。當年輕代被填滿的時候,會導致一次“小回收”(譯注:原文minor,似乎“未成年”更貼切一些,不過咱們讀起來會很別扭),這里只有年輕代的對象惠北回收,而其他年齡斷的垃圾則不與理會。該回收算法的成本是,一階情況下,正比于被回收的活的對象的數量;年輕代因為滿是死對象,所以回收非常迅速。而在“小回收”中存活下來的對象于是乎就會被轉移到所謂的年老代(tenured generation)。最終,當年老代被填滿而需要回收的時候,就會導致一次主回收,這時整個堆都會被回收。主回收通常會運行锝比小回收慢很多,因為大量的對象都會被處理。

如上文記述,對不同的應用,“工效學”會動態選擇垃圾收集器來提供較好的性能。串行垃圾收集器用于哪些數據量比較小的程序,而且它的缺省參數也讓大多數小程序能夠高效工作。而大吞吐量垃圾收集器用于那些有中到大數據量的數據集。工效學選擇的堆尺寸參數和自適應尺寸策略用于為服務器提供更好的性能。這些選擇的大多數而不是所有的情況下工作得很不錯。這就引出了本文的核心宗旨:

如果垃圾收集器成為了瓶頸,你可能不得不調整整個堆的大小乃至每個代的尺寸。檢查垃圾收集器的詳細輸出,然后檢查垃圾收集器對你關注的各個性能指標的影響。

(并行垃圾收集器之外的)缺省的代排布大概就是這樣的。

初始化的時候,最大的地址空間虛擬地保留住而沒有分配出去,直到真的需要的時候為止。整個保留的對象地址空間被分給了年輕的和年老的代。

年輕代包括“伊甸園”和兩個幸存者空間。大部分對象最初在伊甸園里被分配出來。一個幸存者空間在任意時刻都是空的,作為伊甸園中的活對象的目的地,另一個是用于下一次收集。對象在幸存者空間之間停留到足夠老之后,就會被復制到年老代去了。

另一個和年老代有密切關系的代是永久的(permanent)代,這里保存著虛擬機需要的用來描述那些 Java 語言層面沒有等價物的對象。比如,那些描述類和方法的的對象就存放在永久代。

3.1 性能考慮

對于垃圾回收的性能,主要有兩種量度方法:

  1. 吞吐量。吞吐量是在一段足夠長的時間中,沒有花費在垃圾回收上的時間占總時間的百分比。吞吐量包含了花在空間費配上的時間(不過空間分配速度的調優一般是沒有必要的)。
  2. 延時。延時是由于等待垃圾回收而導致的程序沒有響應的時間。

不同的用戶對垃圾收集有不同的需求。比如,對于一個web server而言,吞吐量是合理的量度,因為垃圾收集帶來的短時時延是可以容忍的,或者說是很容易就被網絡時延所掩蓋了。不過,對于交互的圖形界面程序而言,極短的停頓都會影響用戶的使用體驗。

有些用戶對其他的因素很敏感。Footprint是一個進程的工作集,由頁和cache line來量度。對于內存相對于進程數量很有限的系統而言。Footprint會影響到程序的可伸縮性。Promptness是對象死掉和該塊內存重新可用之間的時間間隔的量度,這是分布式系統的一個重要考慮因素,包括遠程方法調用(RMI)。

總的說,一個特定的代的尺寸選擇是上述這些因素之間的權衡的結果。比如,一個非常大的年輕代的大小可以最大化吞吐律,但會以Footprint、 Promptness和延時作為代價。而年輕代延時可以通過縮小該代的大小來達到最小化,但同樣會損失吞吐量。近似地,調整一個代的尺寸不會影響到其他代的垃圾收集頻率和時延。

沒有一個簡單的方法來設置代的尺寸。最好的選擇由程序使用內存的方式和用戶的需求來決定。這樣,虛擬機對垃圾收集器的選擇并不總是最優的,而且可以通過后面介紹的命令行參數來調整。

3.2 測量

使用應用特定的量度,吞吐量和footprint很容易被測量。例如,web服務器的吞吐量可以使用一個客戶端負載生成器來測量,而該服務器的 footprint 則可以在 Solaris 操作系統中使用 pmap 命令來測量。另一方面,垃圾收集導致的時延可以方便地通過監測虛擬機自己的診斷輸出來估算出來。

命令行參數 -verbos:gc 可以送出每一次垃圾收集時的堆和垃圾收集信息。比如,這是一個大型服務器應用的輸出:

  1. [GC 325407K->83000K(776768K), 0.2300771 secs]  
  2. [GC 325816K->83372K(776768K), 0.2454258 secs]  
  3. [Full GC 267628K->83769K(776768K), 1.8479984 secs] 

這里是兩次小回收和之后的一次主回收。箭頭前后的數字(比如第一行的325407K->83000K)分別指垃圾回收前后的所有活著的對象占用的空間。在小回收之后,這個尺寸之中仍然包含一些沒有被回收的垃圾(死掉的對象)。這些對象要么存在在年老代中,要么被年老或永久代中的對象所引用。

后面的括號中的數字(比如第一行中的 (776768K))是全部提交的堆大小,也就是虛擬己不向操作系統申請內存的情況下,全部 java 對象可用的存儲空間。注意,這個數字不包括幸存者空間中的一個,因為幸存者空間在一個給定時間只有一個可用,同時也不包括永久代的空間,這里面是虛擬機使用的元數據。

最后一個數字(比如 0.2300771 secs)是垃圾收集所用的時間;這個例子里大約是四分之一秒。

第三行中主垃圾回收的格式也是類似的。

-verbos:gc 輸出的格式可能在將來的版本里有所改變。

通過-XX:+PrintGCDetails參數可以查看更多垃圾回收相關的信息。下面是串行垃圾收集器使用該參數打印出來的信息。

  1. [GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs] 

這個信息顯示,這次小回收收回了 98% 的 DefNew 年輕代的數據,64575K->959K(64576K) 并在其上消耗了 0.0457646 secs(大約45毫秒)。

整個堆的占用率下降了大約51% 196016K->133633K(261184K),而且通過最終的時間 0.0459067 secs 顯示在垃圾收集中有輕微的開銷(在年輕代之外的時間)。

選項-XX:+PrintGCTimeStamps會提供每次回收開始時間的時間戳。這對于查看垃圾回收頻率非常有用。

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs] 111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs]  26282K->2311K(32704K), 0.1293306 secs]

如上,垃圾回收在程序運行后111秒開始。小回收同時啟動。信息中還顯示了主回收中的年老代的垃圾回收信息。年老代的空間使用率下降了大約 10% 18154K->2311K(24576K) ,用時 0.1290354(大約130毫秒)。

和 -verbose:gc 一樣,-XX:+PrintGCDetails 的輸出格式在將來的版本里也可能會有所變動。

#p#

4. 代的尺寸

很多參數會應想到代的尺寸。下圖是堆中的提交空間和虛擬空間的差別。虛擬機初始化的時候,整個堆空間都是保留的。保留空間可以通過參數 -Xmx 指定。如果-Xms參數小于-Xmx參數,那么不是所有的保留空間都會立刻提交到虛擬機之中。未提交的空間在途中標記為 virtual。堆的不同部分(永久時間段、年老時間段和年輕時間段)可以按需生長到虛擬空間的限制為止。

一些參數可以調整堆的不同部分的比例,比如參數NewRatio指定年老代對年輕代的比例。這些參數將在下面討論。

4.1 全部堆

注意,下面的關于堆的生長、收縮和缺省堆大小都不適用于并行垃圾收集器,并行垃圾收集器請參考相關章節。不過,用于控制整個堆大小和代尺寸的參數對并行垃圾收集器都是適用的。

因為垃圾收集是發生在代被填滿的時候,所以,吞吐量反比于可用此內存數量??偪捎脙却鏀凳怯绊懤占阅艿淖钪匾蛩亍?/p>

缺省情況下,虛擬己在每次垃圾收集后增加或減少堆尺寸,來盡量保持可用空間對活的對象之間的比例在一個區間之內。這個目標區間通過參數 -XX:MinHeapFreeRatio=<minimum>和-XX:MaxHeapFreeRatio=<maximum& gt;來設置,而總的堆大小的界限由-Xms<min>和-Xmx<max>來確定。這些參數在 32 位 Solaris 系統(SPARC 平臺版本)中的缺省值如下表所示:

  1. Parameter   
  2. Default Value   
  3. MinHeapFreeRatio  
  4. 40  
  5. MaxHeapFreeRatio  
  6. 70  
  7. -Xms  
  8. 3670k  
  9. -Xmx  
  10. 64m 

64位系統中的堆尺寸的參數會大 30% 左右,這個增長用來補償64位系統中更大的對象所帶來的開銷。

通過設置這些參數,當一個代的可用空間低于 40%,虛擬機就會把可用內存擴展到 40%,直到代的最大尺寸。同樣的,如果可用空間超過 70%,代就會被縮小,使得只有 70% 可用空間,直到達到代最小的空間為止。

大型服務器程序在使用這些缺省設置時,經常遇到兩種問題。其一是慢啟動問題,初始的堆尺寸過小,經常需要經歷多次主回收才能達到穩定值。另一個更現實的問題是,對于大多數服務器應用來說,這個缺省的最大堆大小太小了。對于服務器程序而言,設置的一般原則是:

  • 除非遇到了時延問題,給虛擬機盡量多的內存。缺省尺寸(64MB)通常都太小了。
  • 把-Xms 和 -Xmx 設置成相同的值,把最重要的尺寸決定從虛擬機收回來,從而增強可預見性。
  • 一般地,隨著處理器數量的增加而增加內存,因為內存分配可以被并行化。

作為參考,有一個單獨的頁面會介紹各個命令行參數 。

4.2 年輕代

影響位居次席的是用于年輕代的堆比例。年輕代越大,小回收的次數也就越少。不過,在一定的堆大小的情況下,年輕代越大,年老代也就越小,這就增加了主回收的頻率。最佳選擇依賴于應用中分配的對象的生存期分布。

缺省的,年輕代的尺寸由 NewRatio 控制。比如,設置-XX:NewRatio=3意味著年輕代和年老代的比例是1:3。換句話說, eden 和幸存者空間的總和是整個堆大小的四分之一。

參數 NewSize 和 MaxNewSize 約束了年輕代的上下界限??梢园堰@兩個參數設成相同的值來固定年輕代的大小,設置 -Xms 和 -Xmx 一樣來設置堆大小為固定值。這樣可以比使用NewRatio更細粒度地調整年輕代的大小。

4.2.1 幸存者空間

如果需要,SurvivorRatio 可以用來調整幸存者空間的大小,不過這對于性能一般影響不大。比如,-XX:SurvivorRatio=6 會設置幸存者空間和eden的比例是 1:6。換句話說,每個幸存者空間將是 eden 的六分之一,是整個年輕代空間的八分之一(不是七分之一,因為一共有兩個幸存者空間)。

如果幸存者空間過小的話,拷貝收集到的幸存者將會直接溢出到年老代的空間中去。如果幸存者空間太大的話,他們也就是空著浪費掉。每次垃圾收集中,虛擬機會選擇一個對象在成為年老的之前被復制的次數門限。這個門限的設置會保證幸存者空間是半滿的。命令行參數 -XX:+PrintTenuringDistribution 可以顯示這個門限和年輕代中對象的年齡。這對于觀測應用中對象的生存期分布也是有用的。

下面是 SPARC 上的 32 位 Solaris 的各個參數的缺省值,在其他平臺上可能有所差異。

  1. Default Value   
  2. Parameter   
  3. Client JVM   
  4. Server JVM   
  5. NewRatio  
  6. 8  
  7. 2  
  8. NewSize  
  9. 2228K  
  10. 2228K  
  11. MaxNewSize  
  12. not limited  
  13. not limited  
  14. SurvivorRatio  
  15. 32  
  16. 32 

年輕代的最大尺寸通過最大堆尺寸和 NewRatio 計算而得。所謂的“無限制”的缺省值是說這個計算的值不會受到 MaxNewSize 的約束,除非命令行中指定了這個值。

服務應用的設置準則是:

  • 首先確定可以提供給虛擬己的最大堆尺寸。然后根據性能需求來確定年輕代的尺寸,來找到最佳設置。
    • 注意:最大堆尺寸一定要小于系統中的內存數量,以防止過多的缺頁錯誤和換頁。
  • 如果總的堆尺寸是確定的,增加年輕代的尺寸就會減少年老代的尺寸。一定要保證年老代的尺寸,使之可以容納所有在應用全程都要用到的活對象,并留有一定裕量(10-20%或更多)。
  • 依照上述年老代的約束:
    • 給年輕代分配足夠的內存。
    • 如果有多個處理器,那么分配更多的內存給年輕代,因為內存分配可以并行化。

5. 可用的垃圾收集器

到目前為止,我們討論的還都是串行垃圾收集器。不過 Java HotSpot 虛擬機一共支持了三種不同的收集器,每種提供不同的性能特性。

  1. 串行垃圾收集器使用單線程進行所有垃圾收集工作,因為沒有線程間通信的開銷,串行垃圾收集器相當高效。串行垃圾收集器最適合于單處理器系統,因為它不會從多處理器硬件中獲益,盡管在小數據量的應用中(不大于100MB的),它對于多處理器系統也是游泳的。串行垃圾收集器在一定的硬件和操作系統的配置時會缺省使用,也可以顯式地用 -XX:+UseSerialGC 參數來指定。
  2. 并行垃圾收集器(或吞吐垃圾收集器)并行進行小垃圾收集,這會顯著減少垃圾收集的的開銷。它適用于中等或大尺寸數據的運行在多處理器或多線程硬件上的應用。并行垃圾收集器也會在一定的硬件和操作系統配置下被缺省使用,同時,也可以使用 -XX:+UseParallelGC 參數來指定。
    • 更新:“并行壓縮”是 J2SE 5.0 update 6 以上版本的新特性,并在 Java SE 6 之中得到加強,該特性允許主回收也并行收集。如果不使用并行壓縮,主回收仍然會單線程運行,這會嚴重限制系統的可伸縮性。并行壓縮可以使用命令行參數 -XX:+UseParallelOldGC 來打開。
  3. 并發垃圾收集器并發地進行大部分垃圾收集工作(也就是在應用運行當中進行)來盡可能煎炒垃圾收集帶來的應用停頓。它是為哪些擁有中到大量數據的、對響應時間要求高于吞吐量要求的應用,因為最小化時延的技術會讓吞吐能力付出代價。并發垃圾收集器通過 -XX:+UseConcMarkSweepGC 參數來啟用。
5.1 選擇垃圾收集器

除非你的應用有非常嚴酷的時延要求,那么就運行你的應用,并讓系統自己選擇垃圾收集器好了。如果有必要的話,就調整堆的大小來增進性能。如果性能仍然無法達到你的目標,那就按照如下設置來選擇一個垃圾收集器。

  1. 如果應用的數據很少(大約不超過100MB),那么
    • 使用-XX:+UseSerialGC選擇串行垃圾收集器。
  2. 如果應用運行在單處理器系統中,并且沒有什么時延要求,那么
    • 讓虛擬機選擇垃圾收集器,或者
    • 使用-XX:+UseSerialGC選擇串行垃圾收集器。
  3. 如果(a)程序峰值性能是第一位的,并且(b)沒有時延要求,或時延要求是一兩秒或更長,那么
    • 讓虛擬機選擇垃圾收集器,或者
    • 使用-XX:+UseParallelGC選擇并行垃圾收集器,乃至(可選)通過 -XX:+UseParallelOldGC啟用并行壓縮。
  4. 如果響應時間比總體吞吐量更為重要,并且垃圾收集時延需要控制在1秒以內,那么
    • select the concurrent collector with -XX:+UseConcMarkSweepGC. If only one or two processors are available, consider using incremental mode, described below.
    • 通過 -XX:+UseConcMarkSweepGC 參數啟用并發垃圾收集器。進當你有一個或兩個處理器可用的時候,考慮使用下文將要介紹的“增量模式”。

這些指導意見僅僅是選擇垃圾收集器的起點,因為性能依賴于堆的尺寸、應用中活數據的數量,以及處理器的數量和速度。時延參數對這些因素尤為敏感,所以,所謂的1秒門限值只是個大致數值:在很多硬件和數據量的組合情況下,并行垃圾收集器可能會導致停頓時間超過1秒;同樣,在某些組合下,并發垃圾收集器也不能保證停頓小于1秒。

如果推薦的垃圾收集器沒有達到期望的性能,首先應該嘗試堆和代的尺寸,以期達到目標。如果仍然不成功的話,嘗試更換一個垃圾收集器:使用并發垃圾收集器來減少停頓時間,使用并行垃圾收集器來增加多處理器系統中的吞吐量。

#p#

6. 并行垃圾收集器

并行垃圾收集器(也被稱為吞吐量收集器)和串行收集器類似,也是一種分代垃圾收集器;其最大的不同在于它使用了多線程來加快垃圾收集的過程。并行垃圾收集器可以通過參數 -XX:+UseParallelGC 指定。缺省的,只有小回收會并行運行,主回收仍然單線程運行。不過,通過參數-XX:+UseParallelOldGC啟動并行壓縮可以讓主回收和小回收都并行運行,從而進一步減少垃圾收集開銷。

在一個有N個處理器的計算機上,并行垃圾收集器使用N個垃圾收集器線程。不過,這個數量可以在命令行參數里指定(參見下文)。在一臺單處理器的計算機上,由于線程開銷(比如同步),并行垃圾收集器的性能應該不如串行垃圾收集器。然而,當應用程序有中等或大尺寸的堆的時候,它在一個雙處理器的機器上就會略優于串行垃圾收集器,而如果有多于兩個處理器的話,它就能遠勝于串行垃圾收集器。

垃圾收集器線程數的多少可以用-XX:ParallelGCThreads=<N>參數來控制。如果要使用命令行參數顯式調整了堆的尺寸,使用并行垃圾收集器的情況下需要的堆的尺寸和使用串行垃圾收集器情況下的堆的尺寸是一階相等的。使用并行垃圾收集器僅僅是讓小回收造成的停頓更短一些。因為有多個垃圾收集器線程參與小回收的過程,有極少的可能性可能會在將年輕代移動到年老代的過程中造成一些碎片。每個垃圾收集線程都有一塊專屬的年老代的空間,用于年輕代向年老代的移動,將年老代的可用空間劃分為“移動緩沖”(promotion buffer)的過程可能會造成一定的碎片效應。減少垃圾收集器線程的數量可以減少碎片、增加年老代的空間。

6.1 代

正如上面提到的,并行垃圾收集器的代的排布方式和串行垃圾收集器略有不同。其分布如下圖所示。

6.2 功效學

自 J2SE 5.0 以來,并行垃圾收集器成為了server級機器的缺省垃圾收集器,詳細資料可以參考“Garbage Collector Ergonomics”。此外,并行垃圾收集器使用一種自動調整機制來指定期望的行為而不是指定代的大小和其他底層調整細節。這些行為包括:

  • 最大垃圾收集停頓時間
  • 吞吐量
  • Footprint (也就是堆尺寸)

最大停頓時間的目標由參數-XX:MaxGCPauseMillis=<N>來指定。這個參數被解釋為指定停頓時間不得大于< N>毫秒;缺省情況下沒有最大停頓時間目標。如果指定了一個停頓時間目標,堆尺寸和其他垃圾回收相關參數就會被相應調整,以便保持垃圾回收時間小于指定的值。注意,這些調整可能會導致總體吞吐量的降低,而且,在某些情況下,要求的停頓時間目標可能無法達到。

吞吐量目標測量垃圾回收時間和非垃圾回收時間(也就是應用時間)的比例。這個目標時間可以用命令行參數-XX:GCTimeRatio=< N>來指定,這樣,垃圾回收時間和應用時間的比例將是1 / (1 + <N>)。例如,-XX:GCTimeRatio=19設置1/20活5%的時間用于垃圾回收。缺省值是99,目標是1%的時間用于垃圾回收。

最大堆footprint使用已經存在的 -Xmx<N> 參數。此外,如果沒有其他的優化目標的話,垃圾收集器有一個隱式的最小化堆尺寸的目標。

6.2.1 目標的優先級

目標的優先級順序如下:

  1. 最大停頓時間目標
  2. 吞吐量目標
  3. 最小堆尺寸目標

最大停頓時間目標會被首先滿足。僅當最大停頓目標被滿足的情況下,才會去滿足吞吐量目標。類似的,僅當前兩個目標都會滿足的情況下,才會考慮去滿足footprint目標。

6.2.2 時間段尺寸調整

每次垃圾收集結束的時候,垃圾收集器都會更新其保存的平均停頓時間之類的統計參量。同時它會檢查各個目標是否被滿足了,是否有調整代尺寸的需要。這之中的意外情況就是顯式的垃圾收集(比如調用 System.gc())會在統計和調整判斷中被忽略掉。

增加和縮小一個代的大小是通過增加活縮小一個固定的百分比來達到的,這樣一個代要分步來達到需要的尺寸。增加活所見是以不同的比率來進行的。缺省情況下,一次增加 20% 活減少 5%。年輕代和年老代增量的比例分別通過命令行參數 -XX:YoungGenerationSizeIncrement=<Y>和 -XX:TenuredGenerationSizeIncrement=<T>來設定。而縮小比例的要通過 -XX:AdaptiveSizeDecrementScaleFactor=<D>參數來設定。如果增量是X%,那么每次減小量就是 (X/D)%。

如果垃圾收集器決定在啟動的時候增加一個代的大小,會有一個額外的百分比的增量。這個附加的增量隨著收集的次數而減少,不會長期影響。這個額外增量意在提高啟動速度??s小代的尺寸是沒有這個額外的增量。

如果最大停頓時間目標沒有達到,會有且僅有一個代的大小被縮小。如果兩個代都在目標之上,停頓時間較大的那個代會首先被縮小。

如果總體吞吐量目標沒有達到,那么兩個代的大小都會增加。每個都按照各自對垃圾回收時間的貢獻比例分別增加。比如,如果年輕代的垃圾回收時間占去了25%的總垃圾回收時間,并且年輕代的全部增量應該是20%,那么這時它的增量就是5%。

6.2.3 缺省堆尺寸

如果沒有在命令行中進行設置,初始和最大堆尺寸會通過計算機內存計算而得。如下表所示,對大小占用的內存的比例是由參數 DefaultInitialRAMFraction和DefaultMaxRAMFraction來控制的。(表中的 memory 代表計算機的系統內存數量。)

  1. Formula   
  2. Default   
  3. initial heap size  
  4. memory / DefaultInitialRAMFraction  
  5. memory / 64  
  6. maximum heap size  
  7. MIN(memory / DefaultMaxRAMFraction, 1GB)  
  8. MIN(memory / 4, 1GB) 

注意,缺省的最大堆尺寸不會超過1GB,不論系統中到底有多少內存。

6.3 過多的GC時間和OutOfMemory錯誤

當有過多的時間花費在垃圾收集上的時候,并行垃圾收集器會跑出 OutOfMemoryError 錯誤:如果超過 98% 的時間花費在垃圾收集上并且只有 2% 的堆被釋放的話,就會拋出一個 OutOfMemory。這個功能是用來防止堆太小導致程序長時間無法正常工作而設計的。如果必要,這個功能可以使用命令行參數 -XX:-UseGCOverheadLimit來關閉。

6.4 測量

并行垃圾收集器的垃圾收集器詳細輸出和串行垃圾收集器是一樣的。

7. 并發垃圾收集器

并發垃圾收集器適用于那些需要更短的垃圾收集停頓,并能為此付出程序運行期處理器資源的應用。典型情況下,那些擁有較多長期存在的對象(年老代比較大),并且運行在擁有兩個活更多處理器的應用可能會因此獲益。不過,在任何要求很低停頓時間的應用都應該考慮這個垃圾收集器;比如,擁有較小年老代的交互程序在但處理器上使用并發垃圾收集器就可以收到明顯的好處,特別是使用增量模式的時候。并發垃圾收集器可以通過命令行參數 -XX:+UseConcMarkSweepGC來啟動。

和其他垃圾收集器類似,并發垃圾收集器也是分代的;所以也有小回收和主回收。并發垃圾收集器通過使用獨立的垃圾收集線程于應用本身的線程并發執行跟蹤所有可及的對象,以期降低主回收導致的停頓。在每個主回收周其中,并發垃圾收集器會在垃圾收集的開始讓所有應用線程暫停一下,并在回收中段再暫停一次。第二次暫停相對而言會更長一些,在此期間會有多個線程來進行收集工作。剩下的收集工作包括大部分的活對象跟蹤和清除不可及的對象的工作都由一個或多個和應用并發的垃圾收集器線程來進行。小回收會在進行的主回收周其中穿插進行,其模式和并行垃圾收集器十分類似(特別需要說明的就是,在小回收期間,應用線程是會有停頓的)。

并發垃圾收集器的基本算法在技術報告 A Generational Mostly-concurrent Garbage Collector里有介紹。主義,實際的實現細節在不同版本里手有細微的變化的,因為垃圾收集器也在一直進步。

7.1 并發性的開銷

并發垃圾收集器的短主回收停頓時間是以處理器資源作為代價的(這些資源如果不用在收集器上肯定就要用在應用上了)。最明顯的開銷就是并發地使用了一個或多個處理器資源。在N處理器系統中,垃圾收集的并發部分會使用K/N的可用處理器,其中 1<=K<=ceiling{N/4}。(注意,K值的上限將來可能會有變化。)并發垃圾收集器不僅在并發階段使用處理器,還引入了其他的開銷。所以,盡管并發垃圾收集器顯著減少了程序的停頓,但和其他垃圾收集器相比,應用的總體吞吐量會受到輕微的影響。

在擁有多個處理器的計算機上,在并發垃圾收集器運行的時候,應用程序仍然能使用到CPU,所以,并發垃圾收集器并沒有讓程序停頓。這通常意味著更短的停頓,談也意味著更少的應用可用的處理器資源,并且讓它運行得相對比較慢,特別是當應用可以完全的利用多個CPU核心的時候更是如此。隨著N的上升,垃圾收集器導致的損失會相對變小,而從并發垃圾收集的獲益則相對提高。下一節“并發模式失敗”會討論這種規模擴張的潛在局限。

因為在并發階段至少有一個處理器用于了垃圾收集,所以在單處理器(單核)系統中,并發垃圾收集器一般不會帶來什么好處。不過,并發垃圾收集有一個分離模式可以在單處理器或雙處理器系統中顯著減少停頓時間;后面的增量模式中將會進一步介紹其細節。

7.2 并發模式失敗

并發垃圾收集器使用一個或多個垃圾收集線程在應用線程執行的同時運行,從而在年老代和永久代變滿之前就完成垃圾收集。如前文所述,在一般的操作中,并發垃圾收集器的大部分跟蹤與清理工作是在程序運行的同時進行的,所以,程序線程只有極少的停頓。但是,如果并發垃圾收集器在年老代變滿的時候仍沒有完成垃圾清除工作,或是年老代中的可用空間無法滿足一次分配操作的需要的時候,應用就不得不被暫停下來以等待應用線程結束了。這種無法并發地完成垃圾收集的情況被稱為“并發模式失敗”,這就需要對并發垃圾收集器的參數進行調整了。

7.3 過多的GC時間和OutOfMemory錯誤

并發垃圾收集器會在垃圾收集消耗時間過多的時候拋出 OutOfMemoryError 錯誤:如果多于 98% 的時間被花費在了垃圾手機上,并且僅有少于 2% 的堆被回收的話,就會拋出 OutOfMemoryError。這個功能是用來防止堆太小導致程序長時間無法正常工作而設計的。如果必要,這個功能可以使用命令行參數 -XX:-UseGCOverheadLimit來關閉。

這個策略和并行垃圾收集器是基本一致的,惟一的區別就是并發的垃圾收集時間并未計算在內。也就是說,只有哪些程序停頓下來進行垃圾收集的時間才被計算在內了。這些垃圾收集常常是由于并發模式失敗或是顯式垃圾收集請求(如調用 System.gc())導致的。

7.4 浮動垃圾

并發垃圾收集器與 HotSpot 中的其他垃圾收集器一樣,是一種識別至少所有在堆中可以被訪問到的對象的跟蹤收集器。按照Jones and Lins的說法,是一種增量更新(Incremental Update)垃圾收集器。因為應用現成和垃圾收集器線程在主回收過程中并發執行,那么那些垃圾收集器跟蹤的對象就可能在垃圾收集完成之后變成垃圾這些無法訪問卻還沒有被回收的對象被稱為浮動垃圾(floating garbage)。浮動垃圾的數量取決于垃圾收集周期的長度和程序中引用更新的頻率,也被稱為轉化率(mutation)。而且,另一個原因是年輕代和年老代的收集是獨立的,彼此都是對方的根。一個粗略的配置規則是為年老代的浮動垃圾多預留出20%的空間來。一個垃圾回收周期中的堆中的浮動垃圾會在下一個垃圾回收周期中被回收。

7.5 時延(停頓)

并發垃圾收集器在一個并發回收周期中會兩次暫停應用。第一次會從根從根(比如從對象線程棧和寄存器、靜態對象等的引用)和堆的其他部分(如年輕代)開始標記所有直接可達的活的對象。第一次停頓被稱為“初始標記停頓”(initial mark pause)。第二次停頓發生在并發跟蹤階段末尾,用來發現由于在垃圾收集線程跟蹤完一個對象之后又被應用線程更新了其引用而沒有被并發跟蹤到的對象。這次停頓被稱為“重標記停頓”(remark pause)。

7.6 并發階段

可達對象的并發跟蹤圖發生在初始標記停頓和重標記停頓之間。在并發跟蹤階段中,一個或多個并發垃圾收集器線程會使用那些本來可能會被應用使用的處理器資源,所以盡管不會停頓,計算密集型應用可能會在此階段和其他并發階段受到相當的吞吐量損失。在重標記停頓之后,還有一個并發清理階段,會收集所有標記為不可達的對象。一旦手機周期結束了,并發收集器就會進入等待階段,這時就基本不會消耗任何計算資源了,直到下一個主回收周期開始為止。

7.7 開始并發收集周期

在串行收集器中,每當年老代滿了的時候都會引發一次主回收,所有應用現成都會在主回收期間暫停運行。并發垃圾收集器與之不同,它需要在足夠早的時間開始垃圾收集,以便能在年老代變滿之前完成垃圾收集;否則的話就會因為并發模式失敗而導致較長的時延。有很多種條件可以觸發并發垃圾收集器啟動。

基于最近的歷史記錄,并發垃圾收集器維護了一個年老代變滿的預期剩余時間和一個垃圾收集周期的預期時間?;谶@些動態估計,并發垃圾收集周期會以讓垃圾收集周期在年老代變滿之前完成為目標開始并發垃圾收集周期。因為并發模式失敗的代價非常慘重,這些估值都流出了安全裕量。

并發垃圾收集在年老代的已用百分比超出了一個初始占有率值(initiating occupancy)的時候也會啟動。這個初始占有率閾值的缺省值大約是 92%,不過這個值可能在不同版本中略有不同。它也可以通過命令行參數-XX:CMSInitiatingOccupancyFraction=< N> 來手工設置,其中N是一個0-100的整數,代表年老代的占用百分比。

7.8 調度中斷

年輕代和年老代的垃圾收集的停頓發生彼此間是獨立的。他們不會重合,但可能會連續發生,這樣也就讓一個垃圾收集的停頓連上下一個垃圾收集的停頓了,從外界來看就是一個長停頓了。為了避免這種情況,并發垃圾收集器會調度重標記停頓的時間,使之發生在前后兩個年輕代停頓之間。這個調度目前還不應用于初始標記停頓,因為它通常會比重標記停頓短很多。

7.9 增量模式

并發垃圾收集器可以在這樣一種模式下工作:并發階段以增量的方式進行?;貞浺幌?,在并發階段,垃圾回收線程會使用一個或多個處理器。所謂增量模式是指減少長并發階段的影響,周期性中斷并發階段,將處理器資源還給應用程序。這種模式又稱為“i-cms”,將垃圾收集器的并發工作劃分到小塊時間,在年輕代垃圾收集之間進行。這個功能對于那些工作在沒那么多處理器的機器上(1或2個處理器的)需要并發垃圾收集器的低時延應用非常有用。

并發垃圾收集周期通常包括如下幾步:

  • 停止所有的應用線程,標記從根開始可達的對象集,然后繼續所有的應用線程
  • 在應用線程運行的同時,使用一個或更多的處理器,并發跟蹤可達的對象圖
  • 使用一個處理器,并發跟蹤對象圖中在上一步開始之后的各個改動的部分
  • 停止所有的應用線程,重新跟蹤根和對象圖中自從上次檢查開始發生了變化的部分,然后繼續運行線程
  • 使用一個處理器,并發地把不可達對象清理到用于分配空間的 free list 上面去。
  • 使用一個處理器并發地調整堆的大小,準備下一個回收周期所需的數據結構

正常情況下,并發垃圾收集器在并發跟蹤階段使用一個或多個處理器,不會讓出它們。類似的,在清理階段也會始終獨占地使用一個處理器。這對于對于一個程序的響應時間可能是個不小的影響,特別是系統中只有一兩個CPU的時候。增量模式通過將并發階段分解為一系列的突發行為來降低這一影響,這些突發行為會散布在小回收之間。

i-cms 使用占空比來控制并發收集器自發的放棄處理器之前的工作量。占空比是年輕代收集之間的允許并發垃圾收集器運行時間的百分比。i-cms 可以根據應用的行為自動計算占空比(這也是推薦的方法,稱為自動步長(auto pacing)),當然,也可以通過命令行指定一個固定的值。

7.9.1 命令行參數

下面是控制 i-cms的命令行參數(參考下文的初始設置建議):

參數

描述

缺省值

J2SE 5.0 及以前

Java SE 6 及以后

-XX:+CMSIncrementalMode

啟動增量模式。注意,并發垃圾收集器必須也被選擇(-XX:+UseConcMarkSweepGC) ,否則此參數無效。

disabled

disabled

-XX:+CMSIncrementalPacing

打開自動步長,這樣,增量模式占空比將根據JVM統計到的信息自動調整。

disabled

enabled

-XX:CMSIncrementalDutyCycle=<N>

兩次小回收之間的允許并發收集器運行的時間的百分比(0-100)。如果打開自動步長,那么這個值就是初始值。

50

10

-XX:CMSIncrementalDutyCycleMin=<N>

自動步長打開后,占空比值的下限 (0-100)。

10

0

-XX:CMSIncrementalSafetyFactor=<N>

計算占空比值時使用的一個裕量(0-100)

10

10

-XX:CMSIncrementalOffset=<N>

在小回收之間,增量模式中占空比開始的時間,或說是向右的平移量(0-100)

0

0

-XX:CMSExpAvgFactor=<N>

當進行并發回收統計,計算指數平均值時,當前采樣所用的權值(0-100)

25

25

7.9.2 建議參數

要在 Java SE 6 里使用 i-cms,需要使用如下命令行參數

  1. -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \  
  2. -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 

前兩個參數分別啟動并發垃圾收集器和 i-cms。后兩個參數不是必須的,它們只是要求垃圾收集器將診斷信息打印到標準輸出,這樣,垃圾收集器的行為就可以被看到并用于以后分析了。

注意,對于 J2SE 5.0 和之前的版本,我們建議 i-cms 使用如下的初始命令行參數:

  1. -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode \  
  2. -XX:+PrintGCDetails -XX:+PrintGCTimeStamps \  
  3. -XX:+CMSIncrementalPacing   
  4. -XX:CMSIncrementalDutyCycleMin=0 
  5. -XX:CMSIncrementalDutyCycle=10 

這樣,就是用了和 Java SE 6 一致的參數了,多出的三個參數用于自動調整占空比。這些多余的參數值完全是使用的 Java SE 6 的缺省值。

#p#

7.9.3 基本問題處理

i-cms 的自動占空比計算模式使用了程序運行時收集到的統計信息進行占空比計算,以保證并發垃圾收集器可以在堆占滿之前完成。不過,使用過去的行為預測將來的變化的估計方式可能并不總是足夠準確,可能在某些情況下無法阻止堆用滿。如果需要收集的垃圾太多,可以嘗試下面這些步驟,一次使用一個:

Step

Options

1. 增加保險系數

-XX:CMSIncrementalSafetyFactor=<N>

2. 增加最小占空比

-XX:CMSIncrementalDutyCycleMin=<N>

3. 關閉自動占空比計算,使用固定占空比

-XX:-CMSIncrementalPacing -XX:CMSIncrementalDutyCycle=<N>

7.10 測量

下面是使用-verbose:gc和-XX:+PrintGCDetails參數時,并發垃圾收集器的輸出,一些小細節已經被去掉了。注意,并發垃圾收集器的輸出里摻雜著小回收的輸出;典型情況下,很多小回收會發生在并發收集周期之中。其中的CMS-initial-mark表征了一個并發垃圾回收周期的開始。CMS-concurrent-mark: 標志著并發標記階段的完成,而CMS-concurrent-sweep則標志著并發清除階段的完成。之前沒有提到過的預清除階段以CMS- concurrent-preclean為標志。預清除可以和重標記階段CMS-remark的準備工作同時運行。最后一個階段是CMS- concurrent-reset,這是下一個并發收集周期的準備工作。

  1. [GC [1 CMS-initial-mark: 13991K(20288K)] 14103K(22400K), 0.0023781 secs]
  2. [GC [DefNew: 2112K->64K(2112K), 0.0837052 secs] 16103K->15476K(22400K), 0.0838519 secs]
  3. ...
  4. [GC [DefNew: 2077K->63K(2112K), 0.0126205 secs] 17552K->15855K(22400K), 0.0127482 secs]  
  5. [CMS-concurrent-mark: 0.267/0.374 secs]
  6. [GC [DefNew: 2111K->64K(2112K), 0.0190851 secs] 17903K->16154K(22400K), 0.0191903 secs]  
  7. [CMS-concurrent-preclean: 0.044/0.064 secs]  
  8. [GC [1 CMS-remark: 16090K(20288K)] 17242K(22400K), 0.0210460 secs]  
  9. [GC [DefNew: 2112K->63K(2112K), 0.0716116 secs] 18177K->17382K(22400K), 0.0718204 secs]  
  10. [GC [DefNew: 2111K->63K(2112K), 0.0830392 secs] 19363K->18757K(22400K), 0.0832943 secs]  
  11. ...  
  12. [GC [DefNew: 2111K->0K(2112K), 0.0035190 secs] 17527K->15479K(22400K), 0.0036052 secs]  
  13. [CMS-concurrent-sweep: 0.291/0.662 secs]  
  14. [GC [DefNew: 2048K->0K(2112K), 0.0013347 secs] 17527K->15479K(27912K), 0.0014231 secs]  
  15. [CMS-concurrent-reset: 0.016/0.016 secs]  
  16. [GC [DefNew: 2048K->1K(2112K), 0.0013936 secs] 17527K->15479K(27912K), 0.0014814 secs] 

初始標記停頓在典型情況下比小回收的停頓時間還要小。而如上例所示,并發階段(并發標記、并發預清除和并發清除)通常會比小回收長很多。不過注意,應用并沒有在這些并發階段中停頓下來。重標記停頓通常和一個小回收的長度相當。重標記停頓揮手道應用的某些特征(如高對象修改頻率可能會增加這個停頓)和上一次小回收的時間(即,更多的年輕代對象可能會增加這個停頓)的影響。

8. 其他考慮

8.1 永久代尺寸

在大部分應用中,永久代對于垃圾回收性能沒有顯著的影響。不過,一些應用會動態的生成與加載很多類;比如,一些 JavaServer Pages(JSP)頁面的實現。這些應用可能需要很大的永久代去存放一些多余的類。如果這樣的話,最大永久代的尺寸可以用命令行參數 -XX:MaxPermSize=<N>來增大。

8.2 Finalization; Weak, Soft and Phantom References

一些應用使用 finalization 和 weak, soft, phantom 引用與垃圾收集器交互。這些特征可以 Java 語言層帶來性能影響。一個例子是通過 finalization 來關閉文件描述符,這會導致一個外部資源依賴于垃圾收集器。以來垃圾收集器來管理內存之外的資源是個壞主意。

參考資料章節中的文章深度討論了一些finalization的常見錯誤和用來避免這些錯誤的技術。

8.3 顯式垃圾回收

應用程序和垃圾回收器的另一個交互途徑是顯式調用 System.gc() 進行完整的垃圾回收。這回強制進行一次主回收,即使沒有必要(也就是說一次小回收可能就足夠了),所以應該避免這種情況。顯式垃圾回收對性能的影響可以通過使用 -XX:+DisableExplicitGC 進行比較來進行測量,這樣虛擬機會無視 System.gc() 的。

最常見的顯式調用垃圾回收的場景是 RMI 的分布式垃圾回收。使用 RMI 的應用會引用到其他虛擬機中的對象。在這種分布式應用的場景下,本地堆中的垃圾可能不能被回收掉,所以 RMI 會周期性強制進行完整的垃圾回收。這些回收的頻率可以使用參數來控制。如

java -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 …

這里指定了垃圾回收每小時運行一次,而不是缺省的每分鐘一次。不過,這可能會導致某些對象的清除消耗太長時間。這些參數可以被設置到高達Long.MAX_VALUE來讓顯式垃圾回收的間隔時間無限長,如果沒有合適的DGC上限時間的話。

8.4 Soft References

Soft reference在虛擬機中比在客戶集中存活的更長一些。其清除頻率可以用命令行參數 -XX:SoftRefLRUPolicyMSPerMB=<N>來控制,這可以指定每兆堆空閑空間的 soft reference 保持存活(一旦它不強可達了)的毫秒數,這意味著每兆堆中的空閑空間中的 soft reference 會(在最后一個強引用被回收之后)存活1秒鐘。注意,這是一個近似的值,因為 soft reference 只會在垃圾回收時才會被清除,而垃圾回收并不總在發生。

8.5 Solaris 8 替換 libthread

Solaris 8 操作系統提供了一個替代的線程庫,libthread, 它將線程直接綁定成了輕量級進程(LWP)。有些應用能夠從中極大獲益,并潛在的對所有多線程應用都或多或少的有好處。下面的命令會為 java 啟用替換的 libthread(BASH 格式)

LD_PRELOAD=/usr/lib/lwp/libthread.so.1
export LD_PRELOAD
java ...

這個方法僅對 Solaris 8 適用,因為對 Solaris 9 操作系統來說,這是缺省的,而 Solaris 10 中,這是惟一的線程庫。

9. 相關資源

  1. HotSpot VM Frequently Asked Questions (FAQ)
  2. GC output examples 介紹了如何解釋不同垃圾收集器的輸出。
  3. How to Handle Java Finalization’s Memory-Retention Issues 介紹了一些容易犯的錯誤和避免他們的方法。
  4. Richard Jones and Rafael Lins, Garbage Collection: Algorithms for Automated Dynamic Memory Management, Wiley and Sons (1996), ISBN 0-471-94148-4

在本網站中,名詞“Java Virtual Machine” 和“JVM” 都代表 Java 平臺虛擬機。

英文原文:http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

本文來自:http://wangxu.me/blog/p/209

 

責任編輯:林師授 來源: wangxu.me
相關推薦

2015-07-06 10:14:25

Java垃圾回收實戰

2014-12-19 11:07:40

Java

2012-01-09 16:53:36

JavaJVM

2020-04-22 21:44:18

Java虛擬機算法

2023-11-23 09:26:50

Java調優

2021-10-05 20:29:55

JVM垃圾回收器

2012-01-10 11:19:35

JavaJVM

2010-09-26 11:22:22

JVM垃圾回收JVM

2012-01-09 17:06:16

JavaJVM

2020-05-14 13:39:19

Java 垃圾回收機制

2010-11-05 09:47:11

OracleJava虛擬機

2021-02-04 10:43:52

開發技能代碼

2021-09-10 00:34:22

Java 線程啟動

2012-01-10 14:25:36

JavaJVM

2021-01-04 10:08:07

垃圾回收Java虛擬機

2020-09-02 07:03:04

虛擬機HotSpotJava

2025-01-24 00:00:00

JavaHotSpot虛擬機

2023-02-26 11:50:04

Hbase程序Oracle

2009-06-18 13:59:33

Java SE 6垃圾回收器

2012-05-18 10:22:23

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久国产一区二区三区四区小说 | 日本成人中文字幕 | 亚洲一区中文字幕在线观看 | 1000部精品久久久久久久久 | 亚洲精品一区二区冲田杏梨 | 亚洲欧美中文字幕在线观看 | 午夜免费 | 欧美日韩久久 | 国产做爰 | 超碰人人91 | 精品欧美乱码久久久久久1区2区 | 一级a性色生活片久久毛片 一级特黄a大片 | 精品无码久久久久久久动漫 | 免费高清av | 亚洲超碰在线观看 | 韩日一区二区 | 国产欧美一区二区三区在线看 | 国产丝袜一区二区三区免费视频 | 91成人| 国产在线激情视频 | 一二区成人影院电影网 | 欧美成人a∨高清免费观看 色999日韩 | 91精品一区二区三区久久久久 | 综合久久综合久久 | 黄色大片免费播放 | xxx.在线观看 | 欧美精品日韩 | 精品国产乱码久久久久久牛牛 | 午夜寂寞福利视频 | 91五月婷蜜桃综合 | 亚洲精品第一国产综合野 | 国产91综合一区在线观看 | 伊人网站在线观看 | 成人av片在线观看 | 99久久精品免费 | 午夜影视 | 日本成人免费网站 | 亚洲精品一区在线观看 | 中文字幕一区二区三区在线观看 | 日韩在线欧美 | 日本大香伊一区二区三区 |