Java編程是內存受限模式下的編程, 內存對于 Java 程序來說,是非常有限的資源,所以了解Garbage Collection的機制很重要。
我們只要加入java的啟動參數 -verbose:gc,就可以用查看垃圾回收的情況。
下面是有關參數的幾個說明
-verbose:gc在虛擬機發生內存回收時在輸出設備顯示信息,該參數用來監視虛擬機內存回收的情況。
-Xnoclassgc
關閉虛擬機對class的垃圾回收功能。
-Xincgc
啟動增量垃圾收集器,缺省是關閉的。增量垃圾收集器能減少偶然發生的長時間的垃圾回收造成的暫停時間。但增量垃圾收集器和應用程序并發執行,因此會占用部分CPU在應用程序上的功能。
-Xloggc:<file>
將虛擬機每次垃圾回收的信息寫到日志文件中,文件名由file指定,文件格式是平文件,內容和-verbose:gc輸出內容相同。
JVM管理的內存,通常叫做堆(heap),如果 JVM 不能在 java 堆中獲得更多內存來分配更多 java 對象,將會拋出 java 內存不足錯誤。如果 java 堆充滿了活動對象,并且 JVM 無法再擴展 java 堆,那么它將不能分配更多 java 對象。
在這種情況下,JVM就出現 java.lang.OutOfMemoryError 。
JVM啟動后,保留一段地址空間,這個空間的大小由-Xmx指定。這塊空間的大小就是heap可能的最大值,但一開始不一定全都分配了物理內存,初始分配的heap大小由-Xms指定,一般的jdk都有最小值,如果-Xms小于-Xmx,需要的時候,向OS申請
當JVM進行GC的時候,是要消耗CPU資源和需要一定時間的,這會影響到程序的正常運行,因此需要盡可能減少GC消耗的時間。
Java程序運行過程中,對象的生命周期有長有短,所以Java Heap分為3個區,Young,Old和Permanent。當Young被填滿時,GC會將對象移到Old區。Permanent是一個永久區域,分配給JVM,可以通過 命令行參數 -XX:MaxPermSize=64m 來設置,permanent generation添滿后導致Full GC。
young generation由一塊Eden和兩塊Survivor Space構成。新創建的對象的內存都分配自eden。一塊Survivor Space是空閑的,用作copying collection的目標空間。Minor collection的過程就是將eden和在用survivor space中的活對象copy到空閑survivor space中。對象在survivor spaces 里經歷了數次copied后,就會被移到tenured generation或tenured掉。
GC用較高的頻率對young generation進行掃描和回收,這種叫做minor collection,而對old generation的檢查回收頻率要低很多,稱為major collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍。
老版本的JVM是分代垃圾收集(generational garbage collection),有兩種收集方式:
第一個是scavenge,將所有活的對象搬到另外一塊內存后,整塊內存回收。這種方法有效率,但需要有一定的空閑內存,拷貝也有開銷,用于minor collection。
第二個是mark-compact,將活的對象標記出來,然后搬遷到一起連成大塊的內存,其他內存就可以回收了。這種方法不需要額外的空間,速度會慢一些,方法用于major collection。
注意這兩種GC機制都不是并行的,是單線程的,GC運行時,所有的用戶線程將暫停,Java應用程序不做任何工作,web應用停止響應。
JDK 1.4.1 中新的收集器都是為解決多處理器系統中垃圾收集器的問題而設計的。因為大多數垃圾收集算法會在一段時間里使系統停止,單線程的收集器很快會成為伸縮性瓶頸,因為在垃圾收集器將用戶程序線程掛起時,除了一個處理器之外,其他的處理器都是空閑的。新收集器中的兩個――并行復制收集器和并發標記-清除收集器――設計為減少收集暫停時間。另一個是并行清除收集器,它是為在大堆上的更高吞吐能力而設計的。
并行復制收集器用 JVM 選項 -XX:+UseParNewGC 啟用,是一個年輕代復制收集器,它將垃圾收集的工作分為與 CPU 數量一樣多的線程。并發標記-清除收集器由 -XX:+UseConcMarkSweepGC 選項啟用,它是一個老代標記-清除收集器,它在初始標記階段(及在以后暫短重新標記階段)暫短地停止整個系統,然后恢復用戶程序,同時垃圾收集器線程與用戶程序并發地執行。并行復制收集器和并發標記-清除收集器基本上是默認的復制收集器和標記-整理收集器的并發版本。由 -XX:+UseParallelGC 啟用的并行清除收集器是年輕代收集器,針對多處理器系統上非常大(吉字節以及更大的)堆進行了優化。
JDK 1.4.1 還包括大量的微調垃圾收集的選項。調整這些選項并衡量它們的效果可能會花費您大量時間,因此在試圖微調垃圾收集器之前先對您的應用程序進行徹底的配置(profile)和優化,這樣您的微調工作可能會得到更好的結果。
微調垃圾收集首先要做的是檢查冗長的 GC 輸出。這會使您得到垃圾收集操作的頻率、定時和持續時間等信息。最簡單的垃圾收集微調就是擴大最大堆的大小( -Xmx )。隨著堆的增大,復制收集會變得更有效,所以在增大堆時,您就減少了每個對象的收集成本。除了增加最大堆的大小,還可以用選項 -XX:NewRatio 增加分配給年輕代的空間份額。也可以用 -Xmn 選項顯式指定年輕代的大小。
注意:大的收集(major collection)既會收集年輕的代,也會收集老的代。 System.gc() 方法總是觸發一個大的收集,這就是應該盡量少用(如果不能完全不用的話) System.gc() 的原因之一,因為大的收集要比小的收集花費長得多的時間。沒有辦法以編程方式觸發小的收集,最好使用-XX:+DisableExplicitGC禁止ExplicitGC。
JVM的很多參數會影響里面各部分空間的分配。-XX:MinHeapFreeRatio與-XX:MaxHeapFreeRatio設定空閑內存占總內存的比例范圍,這兩個參數會影響GC的頻率和單次GC的耗時。-XX:NewRatio決定young與old generation的比例,設置-XX:NewRatio=3意味著old和 young generation是1:3。Young generation空間越大,minor collection頻率越低,但是old generation空間小了,又可能導致major collection頻率增加。一般來說,eden大小在maximum heap大小的1/4到1/3之間,old generation的大小一定要大于young generation。
-XX:NewSize和-XX:MaxNewSize直接指定了young generation的最小值和最大值。
如果是Server-Side應用,請加 -server 參數。這樣,缺省的NewRatio 是2,SurvivorRatio 是 25 ,適合大部分應用。也可以用NewSize、MaxNewSize來設置。 設置- Xms 和 -Xmx 的大小相等,可以避免在每次 GC 后調整堆內存的大小。同樣道理設置NewSize、MaxNewSize相等。
對于web應用還要注意,由于經常生成和加載類,permanent generation對GC的影響較大,應設置的大些。如果應用程序中有長期活對象,盡可能減少這些對象的存在期。例如,調整 HTTP 會話超時值將有助于更快地回收空閑會話對象。存泄漏的一個例子是在應用服務器中使用數據庫連接池。當使用連接池時,必須在 finally 塊中顯式關閉 JDBC 語句和結果集對象。這是因為,當從池中調用連接對象上的 close() 時,只是簡單地把連接返回池中以供重用,并沒有實際關閉連接和關聯的語句/結果集對象。如果使用全局緩存,最好使用 Week Reference,例如用 WeakHashMap 代替 HashMap 。
posted on 2010-10-28 14:45
Daniel 閱讀(793)
評論(0) 編輯 收藏 所屬分類:
CoreJava