1. 程式人生 > >Tomcat配置JVM引數步驟

Tomcat配置JVM引數步驟

這裡向大家描述一下如何使用Tomcat配置JVM引數,Tomcat本身不能直接在計算機上執行,需要依賴於硬體基礎之上的作業系統和一個java虛擬機器。您可以選擇自己的需要選擇不同的作業系統和對應的JDK的版本,但還是推薦您使用Sun公司釋出的JDK。

Tomcat配置JVM引數

Tomcat本身不能直接在計算機上執行,需要依賴於硬體基礎之上的作業系統和一個java虛擬機器。您可以選擇自己的需要選擇不同的作業系統和對應的JDK的版本(只要是符合Sun釋出的Java規範的),但我們推薦您使用Sun公司釋出的JDK。確保您所使用的版本是最新的,因為Sun公司和其它一些公司一直在為提高效能而對java虛擬機器做一些升級改進。一些報告顯示JDK1.4在效能上比JDK1.3提高了將近10%到20%。

可以給Java虛擬機器設定使用的記憶體,但是如果你的選擇不對的話,虛擬機器不會補償。可通過命令列的方式改變虛擬機器使用記憶體的大小。如下表所示有兩個引數用來設定虛擬機器使用記憶體的大小。

引數      描述

-Xms      JVM初始化堆的大小

-Xmx      JVM堆的最大值

這兩個值的大小一般根據需要進行設定。初始化堆的大小執行了虛擬機器在啟動時向系統申請的記憶體的大小。一般而言,這個引數不重要。但是有的應用程式在大負載的情況下會急劇地佔用更多的記憶體,此時這個引數就是顯得非常重要,如果虛擬機器啟動時設定使用的記憶體比較小而在這種情況下有許多物件進行初始化,虛擬機器就必須重複地增加記憶體來滿足使用。

由於這種原因,我們一般把-Xms和-Xmx設為一樣大,而堆的最大值受限於系統使用的實體記憶體。一般使用資料量較大的應用程式會使用持久物件,記憶體使用有可能迅速地增長。當應用程式需要的記憶體超出堆的最大值時虛擬機器就會提示記憶體溢位,並且導致應用服務崩潰。因此一般建議堆的最大值設定為可用記憶體的最大值的80%。

Tomcat預設可以使用的記憶體為128MB,在較大型的應用專案中,這點記憶體是不夠的,需要調大。

Windows下,在檔案/bin/catalina.bat,Unix下,在檔案/bin/catalina.sh的前面,增加如下設定:

JAVA_OPTS='-Xms【初始化記憶體大小】

-Xmx【可以使用的最大記憶體】'

需要把這個兩個引數值調大。例如:

  1. JAVA_OPTS='-Xms256m-Xmx512m'

表示初始化記憶體為256MB,可以使用的最大記憶體為512MB。

另外需要考慮的是Java提供的垃圾回收機制。虛擬機器的堆大小決定了虛擬機器花費在收集垃圾上的時間和頻度。收集垃圾可以接受的速度與應用有關,應該通過分析實際的垃圾收集的時間和頻率來調整。如果堆的大小很大,那麼完全垃圾收集就會很慢,但是頻度會降低。如果你把堆的大小和記憶體的需要一致,完全收集就很快,但是會更加頻繁。調整堆大小的的目的是最小化垃圾收集的時間,以在特定的時間內最大化處理客戶的請求。在基準測試的時候,為保證最好的效能,要把堆的大小設大,保證垃圾收集不在整個基準測試的過程中出現。

如果系統花費很多的時間收集垃圾,請減小堆大小。一次完全的垃圾收集應該不超過3-5秒。如果垃圾收整合為瓶頸,那麼需要指定代的大小,檢查垃圾收集的詳細輸出,研究垃圾收集引數對效能的影響。一般說來,你應該使用實體記憶體的80%作為堆大小。當增加處理器時,記得增加記憶體,因為分配可以並行進行,而垃圾收集不是並行的。

1:建議用64位作業系統,Linux下64位的jdk比32位jdk要慢一些,但是吃得記憶體更多,吞吐量更大。

2:XMX和XMS設定一樣大,MaxPermSize和MinPermSize設定一樣大,這樣可以減輕伸縮堆大小帶來的壓力。

3:除錯的時候設定一些列印JVM引數,如-XX:+PrintClassHistogram-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintHeapAtGC-Xloggc:log/gc.log,這樣可以從gc.log裡看出一些端倪出來。

4:系統停頓的時候可能是GC的問題也可能是程式的問題,多用jmap和jstack檢視,或者killall-3java,然後檢視java控制檯日誌,能看出很多問題。有一次,網站突然很慢,jstack一看,原來是自己寫的URLConnection連線太多沒有釋放,改一下程式就OK了。

5:仔細瞭解自己的應用,如果用了快取,那麼年老代應該大一些,快取的HashMap不應該無限制長,建議採用LRU演算法的Map做快取,LRUMap的最大長度也要根據實際情況設定。

6:垃圾回收時promotionfailed是個很頭痛的問題,一般可能是兩種原因產生,第一個原因是救助空間不夠,救助空間裡的物件還不應該被移動到年老代,但年輕代又有很多物件需要放入救助空間;第二個原因是年老代沒有足夠的空間接納來自年輕代的物件;這兩種情況都會轉向FullGC,網站停頓時間較長。第一個原因我的最終解決辦法是去掉救助空間,設定-XX:SurvivorRatio=65536-XX:MaxTenuringThreshold=0即可,第二個原因我的解決辦法是設定CMSInitiatingOccupancyFraction為某個值(假設70),這樣年老代空間到70%時就開始執行CMS,年老代有足夠的空間接納來自年輕代的物件。

7:不管怎樣,永久代還是會逐漸變滿,所以隔三差五重起java伺服器是必要的,我每天都自動重起。

8:採用併發回收時,年輕代小一點,年老代要大,因為年老大用的是併發回收,即使時間長點也不會影響其他程式繼續執行,網站不會停頓。

我的最終配置如下(系統8G記憶體),每天幾百萬pv一點問題都沒有,網站沒有停頓,2009年網站沒有因為記憶體問題down過機。

  1. $JAVA_ARGS.="-Dresin.home=$SERVER_ROOT  
  2. -server-Xms6000M-Xmx6000M-Xmn500M  
  3. -XX:PermSize=500M-XX:MaxPermSize=500M
  4. -XX:SurvivorRatio=65536
  5. -XX:MaxTenuringThreshold=0
  6. -Xnoclassgc  
  7. -XX:+DisableExplicitGC  
  8. XX:+UseParNewGC-XX:+UseConcMarkSweepGC  
  9. -XX:+UseCMSCompactAtFullCollection  
  10. -XX:CMSFullGCsBeforeCompaction=0
  11. -XX:+CMSClassUnloadingEnabled  
  12. -XX:-CMSParallelRemarkEnabled  
  13. -XX:CMSInitiatingOccupancyFraction=90
  14. -XX:SoftRefLRUPolicyMSPerMB=0-XX:+PrintClassHistogram  
  15. -XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintHeapAtGC  
  16. -Xloggc:log/gc.log";  

說明一下,-XX:SurvivorRatio=65536,-XX:MaxTenuringThreshold=0就是去掉了救助空間;

-Xnoclassgc禁用類垃圾回收,效能會高一點;

-XX:+DisableExplicitGC禁止System.gc(),免得程式設計師誤呼叫gc方法影響效能;

-XX:+UseParNewGC,對年輕代採用多執行緒並行回收,這樣收得快;

帶CMS引數的都是和併發回收相關的,不明白的可以上網搜尋;

CMSInitiatingOccupancyFraction,這個引數設定有很大技巧,基本上滿足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn就不會出現promotionfailed。在我的應用中Xmx是6000,Xmn是500,那麼Xmx-Xmn是5500兆,也就是年老代有5500兆,CMSInitiatingOccupancyFraction=90說明年老代到90%滿的時候開始執行對年老代的併發垃圾回收(CMS),這時還剩10%的空間是5500*10%=550兆,所以即使Xmn(也就是年輕代共500兆)裡所有物件都搬到年老代裡,550兆的空間也足夠了,所以只要滿足上面的公式,就不會出現垃圾回收時的promotionfailed;

SoftRefLRUPolicyMSPerMB這個引數我認為可能有點用,官方解釋是softlyreachableobjectswillremainaliveforsomeamountoftimeafterthelasttimetheywerereferenced.

Thedefaultvalueisonesecondoflifetimeperfreemegabyteintheheap,我覺得沒必要等1秒;

網上其他介紹JVM引數的也比較多,估計其中大部分是沒有遇到promotionfailed,或者訪問量太小沒有機會遇到,(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn這個公式絕對是原創,真遇到promotionfailed了,還得這麼處理。

JVM記憶體設定原理

預設的java虛擬機器的大小比較小,在對大資料進行處理時java就會報錯:java.lang.OutOfMemoryError。設定jvm記憶體的方法,對於單獨的.class,可以用下面的方法對Test執行時的jvm記憶體進行設定。

java-Xms64m-Xmx256mTest

-Xms是設定記憶體初始化的大小

-Xmx是JVM記憶體設定中設定最大能夠使用記憶體的大小(最好不要超過實體記憶體大小)

在weblogic中,可以在startweblogic.cmd中對每個domain虛擬記憶體的大小進行設定,預設的設定是在commEnv.cmd裡面。簡單介紹了JVM記憶體設定,下面我們看一下JVM記憶體的調優。

JVM記憶體的調優

1.Heap設定與垃圾回收

JavaHeap分為3個區,Young,Old和Permanent。Young儲存剛例項化的物件。當該區被填滿時,GC會將物件移到Old區。Permanent區則負責儲存反射物件,本文不討論該區。JVM的Heap分配可以使用-X引數設定,

JVM有2個GC執行緒。

第一個執行緒負責回收Heap的Young區。

第二個執行緒在Heap不足時,遍歷Heap,將Young區升級為Older區。Older區的大小等於-Xmx減去-Xmn,不能將-Xms的值設的過大,因為第二個執行緒被迫執行會降低JVM的效能。

為什麼一些程式頻繁發生GC?有如下原因:

◆程式內呼叫了System.gc()或Runtime.gc()。

◆一些中介軟體軟體呼叫自己的GC方法,此時需要設定引數禁止這些GC。

◆Java的Heap太小,一般預設的Heap值都很小。

◆頻繁例項化物件,Release物件。此時儘量儲存並重用物件,例如使用StringBuffer()和String()。

如果你發現每次GC後,Heap的剩餘空間會是總空間的50%,這表示你的Heap處於健康狀態。許多Server端的Java程式每次GC後最好能有65%的剩餘空間。

經驗之談:

1.Server端JVM最好將-Xms和-Xmx設為相同值。為了優化GC,最好讓-Xmn值約等於-Xmx的1/3[2]。

2.一個GUI程式最好是每10到20秒間執行一次GC,每次在半秒之內完成[2]。

注意:

1.增加Heap的大小雖然會降低GC的頻率,但也增加了每次GC的時間。並且GC執行時,所有的使用者執行緒將暫停,也就是GC期間,Java應用程式不做任何工作。

2.Heap大小並不決定程序的記憶體使用量。程序的記憶體使用量要大於-Xmx定義的值,因為Java為其他任務分配記憶體,例如每個執行緒的Stack等。

2.Stack的設定

每個執行緒都有他自己的Stack。

Xss每個執行緒的Stack大小

Stack的大小限制著執行緒的數量。如果Stack過大就好導致記憶體溢漏。-Xss引數決定Stack大小,例如-Xss1024K。如果Stack太小,也會導致Stack溢漏。

3.硬體環境

硬體環境也影響GC的效率,例如機器的種類,記憶體,swap空間,和CPU的數量。
如果你的程式需要頻繁建立很多transient物件,會導致JVM頻繁GC。這種情況你可以增加機器的記憶體,來減少Swap空間的使用[2]。

4.4種GC

第一種為單執行緒GC,也是預設的GC。,該GC適用於單CPU機器。

第二種為ThroughputGC,是多執行緒的GC,適用於多CPU,使用大量執行緒的程式。第二種GC與第一種GC相似,不同在於GC在收集Young區是多執行緒的,但在Old區和第一種一樣,仍然採用單執行緒。-XX:+UseParallelGC引數啟動該GC。

第三種為ConcurrentLowPauseGC,類似於第一種,適用於多CPU,並要求縮短因GC造成程式停滯的時間。這種GC可以在Old區的回收同時,執行應用程式。-XX:+UseConcMarkSweepGC引數啟動該GC。

第四種為IncrementalLowPauseGC,適用於要求縮短因GC造成程式停滯的時間。這種GC可以在Young區回收的同時,回收一部分Old區物件。-Xincgc引數啟動該GC。

4種GC的具體描述參見[3]。關於JVM記憶體設定的內容就介紹到這裡,請關注本節其他相關報道。

文章來自:http://developer.51cto.com/art/201009/227004.htm