標籤:
java gc的工作原理、如何最佳化GC的效能、如何和GC進行有效互動
一個優秀的Java 程式員必須瞭解GC 的工作原理、如何最佳化GC的效能、如何和GC進行有效互動,因為有一些應用程式對效能要求較高,例如嵌入式系統、即時系統等。只有全面提升記憶體的管理效 率,才能提高整個應用程式的效能。 本篇文章首先簡單介紹GC的工作原理,然後再對GC的幾個關鍵問題進行深入探討,最後提出一些Java程式設計建議,從GC角度提高Java程式的效能。
GC的基本原理
Java的記憶體管理實際上就是對象的管理,其中包括對象的分配和釋放,對於程式員來說,指派至使用new關鍵字;釋放對象時,只要將對象所有引用賦值為null,讓程式不能夠再訪問到這個對象,我們稱該對象為"不可達的".GC將負責回收所有"不可達"對象的記憶體空間。
對於GC來說,當程式員建立對象時,GC就開始監控這個對象的地址、大小以及使用方式。通常,GC採用有向圖的方式記錄和管理堆(heap)中的所有對象,通過這種方式確定哪些對象是"可達的",哪些對象是"不可達的"。當GC確定一些對象為"不可達"時,GC就有責任回收這些記憶體空間。但是,為了保證GC能夠區別平台實現的問題,Java規範標準對GC的很多行為都沒有進行嚴格的規定。例如,對於採用什麼類型的回收演算法、什麼時候進行回收等重要問題都沒有明確的規定。因此,不同的JVM的實現者往往有不同的實現演算法。這也給Java程式員的開發帶來很多不確定性。本文研究了幾個和GC工作相關的問題,努力減少這種不確定性給Java程式帶來的負面影響。
@@增量式GC( Incremental GC )
GC在JVM中通常由一個或一組進程來實現,它本身也和使用者程式一樣佔用heap空間,運行時也佔用CPU,當GC進程運行時,應用程式停止運行。因此,當GC已耗用時間較長時,使用者能夠感到Java程式的停頓,另一方面,如果GC已耗用時間太短,可能對象回收率太低,這意味著還有很多應該回收的對象沒有被回收,仍然佔用大量記憶體。因此,在設計GC的時候,就必須在停頓時間和回收率之間進行權衡。
一個好的GC實現允許使用者定義自己所需要的設定,例如記憶體有限的裝置,對記憶體的使用量非常敏感,希望GC能夠準確的回收記憶體,它並不在意程式速度的放慢,另外一些即時網路遊戲,就不能夠允許程式有長時間的中斷。 增量式GC就是通過一定的回收演算法,把一個長時間的中斷,劃分為很多個小的中斷,通過這種方式減少GC對使用者程式的影響。雖然,增量式GC在整體效能上可能不如普通GC的效率高,但是它能夠減少程式的最長停頓時間。
Sun JDK提供的HotSpot JVM就能支援增量式GC。HotSpot JVM預設GC方式為不使用增量GC,為了啟動增量GC,我們必須在運行Java程式時增加-Xincgc的參數。HotSpot JVM增量式GC,實現是採用Train GC演算法,它的基本想法:將堆中的所有對象按照建立和使用方式進行分組(分層),將使用頻繁和具有相關性的對象放在一隊中,隨著程式的運行,不斷對組進行調整,當GC運行時,它總是先回收最老的(最近很少訪問的)對象,如果整組都為可回收對象,GC將整組回收,這樣,每次GC運行只回收一定比例的不可達對象,保證程式的順暢運行。
finalize()函數
finalize是位於Object類的一個思路方法,該思路方法的存取修飾詞為protected,由於所有類為Object的子類,因此使用者類很容易訪問到這個思路方法。由於,finalize函數沒有自動實現鏈式調用,我們必須手動實現,因此finalize函數的最後一個語句通常是super.finalize()。通過這種方式,我們可以實現從下到上實現finalize的調用,即先釋放自己的資源,然後再釋放父類的資源。
根據Java語言規範標準,JVM保證調用finalize函數之前,這個對象是不可達的,但是,JVM不保證這個函數一定會被調用。另外,規範標準還保證finalize函數最多運行一次。
很多Java初學者會認為這個思路方法類似和C++中的解構函式,將很多個物件、資源的釋放都放在這一函數裡面。其實,這不是一種很好的方式,原因如下:其一,GC為了能夠支援finalize函數,要對覆蓋這個函數的對象作很多附加的工作;其二,在finalize運行完成之後,該對象可能變成可達的,GC還要再檢查一次該對象是否是可達的,因此,使用finalize會降低GC的運行效能;其三,由於GC調用finalize的時間是不確定的,因此通過這種方式釋放資源也是不確定的。
通常,finalize用於一些不容易控制,並且非常重要資源的釋放,例如一些I/O操作、資料連線等,這些資源的釋放對整個應用程式是非常關鍵的。在這種情況下,程式員應該以通過程式本身管理(包括釋放)這些資源為主,以finalize函數釋放資源方式為輔,形成一種雙保險的管理機制,而不應該僅僅依靠finalize來釋放資源。
程式如何和GC進行互動(不懂...)
Java2增強了記憶體管理功能,增加了一個java.lang.ref包,其中定義了3種引用類。這3種引用類分別為SoftReference、WeakReference和PhantomReference.通過使用這些引用類,程式員可以在一定程度和GC進行互動,以便改善GC的工作效率。這些引用類的引用強度介於可達對象和不可達對象之間。
一些Java編碼的建議
根據GC的工作原理,我們可以通過一些竅門技巧和方式,讓GC運行更加有效率,更加符合應用程式的要求。以下就是一些程式設計的幾點建議:
1、最基本的建議就是儘早釋放無用對象的引用。大多數程式員在使用臨時變數的時候,都是讓引用變數在退出活動域(scope)後自動化佈建為null。我們在使用這種方式時候,必須特別注意一些複雜的對象圖,例如數組、隊列、樹、圖等,這些對象之間有相互引用,關係較為複雜。對於這類對象,GC回收它們一般效率較低。如果程式允許,儘早將不用的引用對象賦為null。這樣可以加速GC的工作。
2、盡量少用finalize函數。Finalize函數是Java提供給程式員一個釋放對象或資源的機會,但是,它會加大GC的工作量,因此盡量少採用finalize方式回收資源。
3、注意集合資料類型,包括數組、樹、圖、鏈表等資料結構,這些資料結構對GC來說回收更為複雜。另外,注意一些全域的變數,以及靜態變數,這些變數往往容易引起懸掛對象(dangling reference),造成記憶體浪費。
4、當程式有一定的等待時間,程式員可以手動執行System.gc(),通知GC運行,但是Java語言規範標準並不保證GC一定會執行,此時使用增量式GC可以縮短Java程式的暫停時間。
java 當GC進程運行時,應用程式停止運行嗎??
對於建立大量對象的大型應用程式,JVM 花在垃圾收集(GC)上的時間會非常多。預設情況下,進行 GC 時,整個應用程式都必須等待它完成,這可能要有幾秒鐘甚至更長的時間(Java 應用程式啟動器的命令列選項 -verbose:gc 將導致向控制台報告每一次 GC 事件)。要將這些由 GC 引起的暫停(這可能會影響快速任務的執行)降至最少,應該將應用程式建立的對象的數目降至最低。同樣,在單獨的 JVM 中運行計劃代碼是有協助的。同時,可以試用幾個微調選項以儘可能地減少 GC 暫停。例如,增量 GC 會盡量將主收集的代價分散到幾個小的收集上。當然這會降低 GC 的效率,但是這可能是時間計劃的一個可接受的代價
資料引用:http://www.knowsky.com/362375.html
Java虛擬機器最佳化選項,GC說明
引用 http://blog.sina.com.cn/s/blog_6d003f3f0100lmkn.html
有許多 JVM 選項會影響基準測試。比較重要的選項包括:
* JVM 的類型:伺服器(-server)與客戶機(-client)。
* 確保有足夠的記憶體可用(-Xmx)。
* 使用的垃圾收集器類型(進階的 JVM 提供許多調優選項,但是要小心使用)。
* 是否允許類垃圾收集(-Xnoclassgc)。預設設定是允許類 GC;使用-Xnoclassgc 可能會損害效能。
* 是否執行 escape分析(-XX:+DoEscapeAnalysis)。
*是否支援大頁面堆(-XX:+UseLargePages)。
* 是否改變了線程堆棧大小(例如,-Xss128k)。
* 使用 JIT編譯的方式:總是使用(-Xcomp)、從不使用(-Xint)或只對熱點使用(-Xmixed;這是預設選項,產生的效能最好)。
* 在執行 JIT編譯之前(-XX:CompileThreshold)、後台 JIT 編譯期間(-Xbatch)或分級的JIT 編譯期間(-XX:+TieredCompilation)收集的剖析資料量。
* 是否執行偏向鎖(biasedlocking,-XX:+UseBiasedLocking);注意,JDK 1.6及更高版本會自動執行這個特性。
*是否啟用最近的實驗性效能調整(-XX:+AggressiveOpts)。
* 啟用還是禁用斷言(-enableassertions 和-enablesystemassertions)。
*啟用還是禁用嚴格的本機調用檢查(-Xcheck:jni)。
* 為 NUMA 多 CPU系統啟用記憶體位置最佳化(-XX:+UseNUMA)。
Class Data Sharing類共用.
java5引入了類共用機制,指在java程式第一次啟動時, 最佳化一些最常用的基礎類到一個共用檔案中,暫只支援ClientVM和serialGC.存放在client/classes.jsa中, 這就是為什麼程式在第一次執行較慢的原因. 開啟參數-Xshare.
J2SE6(代號:Mustang野馬)主要設計原則之一就是提升J2SE的效能和擴充能力,主要通過最大程度提升運行效率,更好的垃圾收集和一些用戶端效能來達到。
1、偏向鎖(Biased locking)
Java6以前加鎖操作都會導致一次原子CAS(Compare-And-Set)操作,CAS操作是比較耗時的,即使這個鎖上實際上沒有衝突,只被一個線程擁有,也會帶來較大開銷。為解決這一問題,Java6中引入偏向鎖技術,即一個鎖偏向於第一個加鎖的線程,該線程後續加鎖操作不需要同步。大概的實現如下:一個鎖最初為NEUTRAL狀態,當第一個線程加鎖時,將該鎖的狀態修改為BIASED,並記錄線程ID,當這一線程進行後續加鎖操作時,若發現狀態是BIASED並且線程ID是當前線程ID,則只設定一下加鎖標誌,不需要進行CAS操作。其它線程若要加這個鎖,需要使用CAS操作將狀態替換為REVOKE,並等待加鎖標誌清零,以後該鎖的狀態就變成DEFAULT,常用舊的演算法處理。這一功能可用-XX:-UseBiasedLocking命令禁止。
2、鎖粗化(Lock coarsening)
如果一段代碼經常性的加鎖和解鎖,在解鎖與下次加鎖之間又沒幹什麼事情,則可以將多次加加鎖解鎖操作合并成一對。這一功能可用-XX:-EliminateLocks禁止。
3、自適應自旋(Adaptive spinning)
一般在多CPU的機器上加鎖實現都會包含一個短期的自旋過程。自旋的次數不太好決定,自旋少了會導致線程被掛起和環境切換增加,自旋多了耗CPU。為此Java6中引入自適應自旋技術,即根據一個鎖最近自旋加鎖成功機率動態調整自旋次數。
4、常用大記憶體分布的堆(large page heap)
在大內分頁是x86/amd64架構上用來減小TLB(虛擬位址到物理地址翻譯緩衝)大小的TLB失配率。Java6中的記憶體堆可以使用這一技術。
5、提高數組拷貝效能
對每種類型大小寫一個定製的彙編數組拷貝程式。
6、後台進行代碼最佳化
Background Compilation in HotSpot™ Client Compiler:後台進行代碼最佳化
7、線性掃描寄存器分配演算法(Linear Scan RegisterAllocation):
一種新的寄存器分配策略,基於SSA(static singleassignment),效能提高10%左右。常用的寄存器分配演算法將寄存器分配看作圖著色問題,時間複雜度是O(n^4),不適用於Java的JIT編譯。原來的JVM裡是根據一些本地啟發學習法規則來分配寄存器,效果不太好,Java6中使用的線性掃描寄存器演算法能夠達到與圖顏色演算法相似的效果,並且時間複雜度是線性。
8、並行縮並垃圾收集器(Parallel Compaction Collector)
進行Full GC時使用並行垃圾收集(JDK 5裡原來非Full GC是並行的但FullGC是串列的),使用-XX:+UseParallelOldGC開啟這一功能
9、並行低停頓垃圾收集器(Concurrent Low Pause Collector)
顯式調用gc(如System.gc)時也可以並行進行標記-清掃式垃圾收集,使用-XX:+ExplicitGCInvokesConcurrent開啟。
10、Ergonomics in the 6.0 Java Virtual Machine
自動調整垃圾收集策略、堆大小等配置,這一功能在JDK 5中加入,JDK6中得到顯著增強,SPECjbb2005效能提高70%。
11、boot類裝載器的最佳化
jre中增加一個描述package所在jar檔案的元索引檔案,加快classloader載入類效能,提高案頭Java應用啟動速度(+15%)。記憶體佔用也減少了10%
12、圖形程式最佳化
在jvm啟動之前顯示splash。
OutOfMemoryError是記憶體溢出, 有多種情況會出現記憶體溢出.
1.java堆溢出java.lang.OutOfMemoryError: Java heapspace.
2.java永久堆溢出,通常是反射,代理用的較多導致類產生過多,java.lang.OutOfMemoryError:PermGen space.
3.本地堆溢出,這可能是由於作業系統無法分配足夠的記憶體,可能是系統已無記憶體,還可能是java進程記憶體空間耗盡,這裡有點意思,一般32位系統進程只有4G地址空間,而又因為java實現使用本地堆或記憶體映射區作為java堆的儲存空間,再去除核心映射區,java使用的堆一般只有2G以內,而如果java堆xmx占的過大,導致jni的本地堆過小,也會產生記憶體溢出.本地堆可以是jni用new,malloc,也可能是DirectBuffer等執行個體.
java.lang.OutOfMemoryError: request<size> bytes for<reason>. Out of swap space?
這時候,如果java堆足夠用的話, 減少xmx的值,反而會解決這種問題.
4.jni方法的溢出.而前者是由jvm檢測的本地溢出,而此是在jni方法調用時,無法分配記憶體.
java.lang.OutOfMemoryError:<reason> <stacktrace> (Native method)
JDK7效能最佳化.
1.(Zero Based )Compressed OOPS
在64位CPU中, JVM的OOP(Ordinary object pointer)為64位,簡單的講,OOP可以被認為為對象的引用,雖然java中基本類型位元是固定的,但參考型別(簡化的C語言指標)用於指向堆中的地址很自然的會被擴充成機器的字長.32位系統最大可訪問記憶體為4G,為了突破這個限制64位系統已經很常見,但是單單引用從32位轉為64位,堆空間佔用大概會增加一半,雖然記憶體已經很便宜,但是記憶體頻寬,CPU緩衝代價是很昂貴的.
CompressedOOPS壓縮可管理的引用到32位以降低堆的佔用空間,在JVM執行時加入編/解碼指令,類似於8086的段管理,其使用
<narrow-oop-base(64bits)> +(<narrow-oop(32bits)><< 3) +<field-offset>公式確定記憶體位址.
JVM在將對象存入堆時編碼,在堆中讀取對象時解碼.
而Zero based compressed oops則進一步將基地址置為0(並不一定是記憶體空間地址為0,只是JVM相對的邏輯地址為0,如可用CPU的寄存器相對定址) 這樣轉換公式變為:
(<narrow-oop << 3) +<field-offset>
從而進一步提高了效能.不過這需要OS的支援.
如果java堆<4G,oops使用低虛擬位址空間,而並不需要編/解碼而直接使用.
Zero based compressed oops針對不同的堆大小使用多種策略.
1.堆小於4G,無需編/解碼操作.
2.小於32G而大於4G,使用Zero based compressed oops
3.大於32G, 不使用compressed oops.
Escape Analysis Improvements
當變數(或者對象)在方法中分配後,其指標有可能被返回或者被全域引用,這樣就會被其他過程或者線程所引用,這種現象稱作指標(或者引用)的逃逸(Escape),也就是說變數不僅僅在本方法內使用.Java對象一般被認為總是在堆中分配的,這使得任何對象都需要進行記憶體回收.而大多數情況下,方法內的對象僅在本方法中使用,完全可以使用棧來儲存,棧內變數釋放是最自然,效能最好的,C中的struct即在分配在棧中.如果實現引用逃逸分析,便可以把沒有引用逃逸的對象分配在棧中,而且不必在語言上加入新的定義方法,引用逃逸分析是自動了.JDK7已經開始預設支援的逃逸分析了.另此還可以消除同步,如果其分析得知對象為非引用逃逸,則所有該對象的同步操作都可以被取消(當然這本是程式員的任務,比如StringBuffer),另可最佳化對象的部分甚至全部都儲存在CPU寄存器內.
NUMA Collector Enhancements
NUMA(Non Uniform MemoryAccess),NUMA在多種電腦系統中都得到實現,簡而言之,就是將記憶體分段訪問,類似於硬碟的RAID,Oracle中的分簇,JVM只不過對此加以應用而矣.
以上三個特性也能在有些JDK6中開啟,具體需要看各版本的changenotes.java6中加入了諸如以下的效能最佳化手段:
輕量鎖 使用cas機制減少鎖的效能消耗.
偏向鎖(biased locking)
鎖粗化(lock coarsening)
由逸出(escape)分析產生的鎖省略 逸出分析還能夠分配記憶體在棧中,以減少記憶體回收的壓力.
自適應自旋鎖(adaptive spinning) 自旋鎖只有在物理多CPU中才會效果.
鎖消除(lock elimination)
在多核CPU中,鎖的擷取比單核系統耗費資源相對大的多, 因為在多核系統中,鎖的擷取需要CPU阻塞資料匯流排,快取寫回.
這樣有時候, 我們在單核系統中,經常會得到StringBuffer與StringBuilder效能差不多的用例, 而且由於有了鎖消除等技術, 有些情況在多核CPU中也會得到效能相差不多的情況.
據信Java7還將預設支援OpenGL的加速功能.
在JDK1.5中加入了Class Data Sharing, 也就是把部分常用的java基本類,緩衝在檔案或共用記憶體中,以供所有java進程使用.
從JRE1.5中,java程式啟動時,如非使用-client|server指令顯示指定,虛擬機器會自動選擇對應的VM,如在64位系統中,只實現了serverVM,所有的虛擬機器都會使用serverVM.32位的系統則windows預設使用clientVM,而Linux,solaris則根據CPU個數和記憶體來確定是否使用serverVM,如jre6以2CPU,2GB實體記憶體為界.
GC
衡量GC效率的參數主要有兩個,一個是輸送量(即效率),一個是停頓時間,另外還有footprint,就是佔用的堆大小.
GC演算法.
1.拷貝,將所有仍然生存的對象搬到另外一塊記憶體後,整塊記憶體就可回收。這種方法有效率,但需要有一定的空閑記憶體,拷貝也有開銷.
2.跟蹤收集器,跟蹤收整合追蹤從根節點開始的對象引用圖。基本的追蹤演算法叫作“標記並清除”,也就是垃圾收集的兩個階段。標記階段,垃圾收集器遍曆引用數,標記每一個遇到的對象。清除階段,未被標記的對象被釋放。可能在對象本身設定標記,要麼就是用一個獨立的位元影像來設定標記。 壓縮(可選),垃圾收集同時要應對磁碟重組的任務。標記和清除通常使用兩種策略來消除堆片段:壓縮和拷貝,這兩種方法都是快速移動對象來減小片段,加在一起叫做mark-sweep-compact.
3.還有一種引用計數收集器,這種方法時堆中的每個對象都有一個引用計數,在引用賦值時加1,置空或作為基本類型的引用超出生命期(如方法退出而棧回收)時減1,其對多個對象的循環參考無能為力,但引用計數都不為0,還有引用數的增減帶來額外開銷,故已不再使用.
分代收集器
根據程式的統計, 大多數對象生命週期都很短,都很快被釋放掉.但也有部分對象生命週期較長, 甚至永久有效.對於拷貝演算法來說,每次收集時,所有的使用中的物件都要移動來移動去。對於短生命的對象還好說,經常可以就地解決掉,可是對於長生命週期的對象就純粹是個體力勞動了,把它挪來挪去除消耗大量的時間,沒有產生任何效益。分代收集能直接讓長生命週期的對象長時間的呆在一個地方按兵不動。GC的精力可以更多的花在收集短命對象上。
這種方法裡,堆被分成兩個或更多的子堆,每一個堆為一“代”物件服務。最年幼的那一代進行最頻繁的垃圾收集。因為多數對象是短命的,只有很小部分的年幼對象可以在經曆第一次收集後還存活。如果一個最年幼的對象經曆了好幾次垃圾收集後仍是活著的,那這個對象就成為壽命更高的一代,它被轉移到另外一個子堆中去。年齡更高一代的收集沒有年輕一代來得頻繁。每當對象在所屬的年齡代中變得成熟(多次垃圾收集後仍倖存)之後,就可以轉移到更高年齡的一代中去。
分代收集一般在年輕堆中應用於拷貝演算法,年老代應用於標記清除演算法。不管在哪種情況下,把堆按照對象年齡分組可以提高最基本的垃圾收集的效能。
一般java中分代收集器將堆分為年輕代, 年老代和永久代.年輕代的收回稱為minorGC,因為在此期內,對象生命週期很較,故效率較高,年老代稱為FullGC,對應的效率較低,用時較長,應盡量減少FullGC的次數.
VM,
Client VM 適合傳統型程式,啟動快, 已耗用時間短, 故其不會預先裝入太多的類,對類進行過多最佳化.
Server VM 適合服務程式,啟動時間不重要,已耗用時間較長, 會預先裝入大多基礎類,對類進行最佳化.
GC種類.
Serial 串列回收器(預設)
在GC運行時,應用邏輯全部暫停,利用單線程通過"拷貝"進行年輕代的垃圾收集,單線程使用"標記-清除-壓縮"進行年老代(tenured)記憶體回收.吞吐率較高.適合單CPU硬體.
Parallel 並行回收器
針對年輕代使用多個GC線程進行"拷貝"垃圾收集,針對年輕代的GC運行時,程式暫停,年老代依然是單線程使用"標記-清除-壓縮"進行年老代記憶體回收,GC運行時,應用同樣暫停.在大記憶體,多處理器的機器上,可以考慮使用此ParallelGC(使用參數-XX:+UseParallelGC指定),這種GC在對YoungGen進行GC時,可以對多處理器加以利用,從而相對降低了停頓時間,但重點是提高了輸送量,但是,在其對OldGen進行GC時,依然使用了和SerialGC同樣的演算法。所以在Jdk5U6中,又引入了Parallel CompactingCollector(使用參數-XX:+UseParallelOldGC指定),這種GC對OldGen的GC也可以受益於多處理器。由於對OldGen的GC遠比YoungGen更耗時間,所以理論上這種GarbageCollector可以提供更優的效能,而且,值得注意的是,Parallel Compacting GC最終會取代ParallelGC。
Concurrent mark-sweep 並發回收器.
對於年輕代使用和多GC線程"拷貝"回收,此GC也需要暫停應用,但由於minorGC效率較高,故不會產生大的停頓,對於年老代使用與應用程式同時啟動並執行並發方式標記-回收機制,其將步驟再次分細,部分階段(初始標記,重新標記)也會完全導致應用暫停,但時間較短,大部分時間都是應用程式與單GC線程並發,降低了應用程式暫停時間。這種GC使用了和ParallelGC一致的YoungGen的收集演算法,而在對OldGen進行GC時,它採用了較為複雜的演算法,提供了極短的停頓時間。但是,複雜的演算法也造成了更大的開銷,而且這種Parallel GC是non-compacting的,所以它使用一個空閑塊鏈表來管理OldGenHeap,分配空間的開銷也加大了.在某些情境中,較短的停頓時間比較大的輸送量更加重要,這時可以考慮使用此GC,即所謂的CMSGC。
增量收集器(Train演算法)已逐漸被棄用,-XincGC 在1.5中會選中並發GC.
在SUN J2SE 5.0中,引入了所謂Behavior-based Parallel CollectorTuning,這種調優方式基於三個Goal:
Maximum Pause Time Goal:使用參數-XX:MaxGCPauseMillis=n指定,預設值為空白。這個參數被指定後,三個記憶體區的GC停頓時間都會儘力的保持在n毫秒以內,如果無法滿足,則相應的記憶體區會縮小,以縮短GC的停頓時間;
Throughput Goal:使用參數-XX:GCTimeRatio=n指定,預設值為99,即GC時間佔總的應用已耗用時間為1%。如果無法滿足,相應的記憶體區會擴大,以提高應用在兩次GC間的已耗用時間;
Footprint Goal: 由於眼下記憶體泛濫,所以這個Goal一般就不值得關注了;
這三個Goal的優先順序為從上到下,即首先滿足Maximum Pause Time Goal,再滿足ThroughputGoal,最後再滿足Footprint Goal。
使用參數-Xloggc:file和-XX:+PrintGCDetails列印gclog,然後使用gcviewer對gclog進行查看,它的優勢在於可以產生統計資料,輸送量,最大的和最小的停頓時間,FullGC時間占整個GC時間的百分比等,都可以使用這個工具查看,但目前只支援到1.5。
JConsole是允許您監測各種各樣的VM資源運行時使用方式的Java監視和管理主控台。實際在java5中, 需要加一個參數,在java6中由於支援了attach API,jconsole會自動載入JVM內部的JMX代理.
jstat命令列印各種各樣的VM統計資料,包括記憶體使用量、記憶體回收時間、類載入和及時編譯器統計。 jmap命令允許您獲得運行時的堆長條圖和堆轉儲。jhat命令允許您分析堆轉儲。jstack命令允許您獲得線程堆疊追蹤。這些診斷工具可以附加到任何應用程式,不需要以特別方式啟動。
引用 http://blog.sina.com.cn/s/blog_6d003f3f0100lmkn.html
java gc的工作原理、如何最佳化GC的效能、如何和GC進行有效互動