這里向大家描述一下如何使用Tomcat配置JVM參數,Tomcat本身不能直接在計算機上運行,需要依賴於硬件基礎之上的操作系統和一個java虛擬機。您可以選擇自己的需要選擇不同的操作系統和對應的JDK的版本,但還是推薦您使用Sun公司發布的JDK。
jvm在client模式,進行內存回收時,會停下所有的其它工作,帶回收完畢才去執行其它任務,在這期間eclipse就卡住了。所以適當的增加jvm申請的內存大小來減少其回收的次數甚至不回收,就會是卡的現象有明顯改善。
主要通過以下的幾個jvm參數來設置堆內存的:
-Xmx512m | 最大總堆內存,一般設置為物理內存的1/4 |
-Xms512m | 初始總堆內存,一般將它設置的和最大堆內存一樣大,這樣就不需要根據當前堆使用情況而調整堆的大小了 |
-Xmn192m | 年輕帶堆內存,sun官方推薦為整個堆的3/8 |
堆內存的組成 | 總堆內存 = 年輕帶堆內存 + 年老帶堆內存 + 持久帶堆內存 |
年輕帶堆內存 | 對象剛創建出來時放在這里 |
年老帶堆內存 | 對象在被真正會回收之前會先放在這里 |
持久帶堆內存 | class文件,元數據等放在這里 |
-XX:PermSize=128m | 持久帶堆的初始大小 |
-XX:MaxPermSize=128m | 持久帶堆的最大大小,eclipse默認為256m。如果要編譯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【可以使用的最大內存】'
需要把這個兩個參數值調大。例如:
- 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過機。
$JAVA_ARGS.="-Dresin.home=$SERVER_ROOT
-server-Xms6000M-Xmx6000M-Xmn500M
-XX:PermSize=500M-XX:MaxPermSize=500M
-XX:SurvivorRatio=65536
-XX:MaxTenuringThreshold=0
-Xnoclassgc
-XX:+DisableExplicitGC
XX:+UseParNewGC-XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSClassUnloadingEnabled
-XX:-CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=90
-XX:SoftRefLRUPolicyMSPerMB=0-XX:+PrintClassHistogram
-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintHeapAtGC
-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內存設置的內容就介紹到這里,請關注本節其他相關報道。