Tomcat中Java垃圾收集調優
設置服務器端的JVM參數一般在catalina.bat文件中:JAVA_OPTS="-server -Xms2048m -Xmx2048m-Xss512k"
JVM中對象的劃分及管理介紹
JVM根據運行於其中的對象的生存時間大致的分爲3種。並且將這3種不同的對象分別存放在JVM從系統分配到的不同的內存空間。這種對象存放空間的管理方式叫做Generation管理方式。
1). Young Generation(年輕代):用於存放“早逝”對象(即瞬時對象)。例如:在創建對象時或者調用方法時使用的臨時對象或局部變量。
2). Tenured Generation(年老代):用於存放“駐留”對象(即較長時間被引用的對象)。往往體現爲一個大型程序中的全局對象或長時間被使用的對象。
3). Perm Generation(永久保存區域):用於存放“永久”對象。這些對象管理着運行於JVM中的類和方法。
------------------------------------------------------------------------------
在命令行下用 java-XmxXXXXM -version 命令來測試java可用最大內存,測試可逐漸增大XXXX的值,如果執行正常就表示指定的內存大小可用,否則會打印錯誤信息。
通常測試windows系統(32位)最大內存爲:1500M,但系統一般到1280M就差不多了
------------------------------------------------------
關於垃圾收集分類介紹
在JVM中有兩種垃圾方式:
1). 一種叫做Minor(次收集)。Minor在YoungGeneration(年輕代)的空間被對象全部佔用後執行,主要是對Young Generation中的對象進行垃圾收集。
2). 一種叫做Major(主收集)。Major是針對於整個Heap size(Xms和Xmx設置爲JVM使用的內存,但不包括永久保存區域使用的內存)的垃圾收集。
其中Minor方式的收集經常發生,並且Minor收集所佔用的系統時間小。而Major方式的垃圾收集則是一種“昂貴”的垃圾收集方式,因爲在Major要對整個Heap size進行垃圾收集,這會使得應用停頓的時間變得較長。
關於TOMCAT內存佔用介紹
Tomcat運行佔用內存= Xmx佔用的內存 + Perm Generation(永久保存區域)佔用內存 + 所有Java應用創建線程數x 1M
Java應用每創建一個線程,在JVM的內存裏也會創建一個Thread對象,但是同時也會在操作系統裏創建一個真正的物理線程(參考JVM規範),操作系統會在TOMCAT餘下的內存裏創建這個物理線程,而不是在JVM的Xmx設置的內存堆裏創建。在jdk1.4裏頭,默認的棧大小是256KB,但是在jdk1.5裏頭,默認的棧大小爲1M每線程。因此,如果系統剩餘內存爲400M的可用內存,則Java應用最多創建400個可用線程。結論:要想創建更多的線程,必須減少分配給JVM的最大內存。
參數說明如下:
-server:一定要作爲第一個參數,在多個CPU時性能佳
-Xms:初始Heap大小,使用的最小內存,cpu性能高時此值應設的大一些
-Xmx:java heap最大值,使用的最大內存
上面兩個值是分配JVM的最小和最大內存,取決於硬件物理內存的大小,建議均設爲物理內存的一半,最大不要超過可用物理內存的80%。
-Xmn:young generation(年輕代)的heap大小,一般設置爲Xmx的3、4分之一(此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8)
(可使用-XX:NewSize和-XX:MaxNewsize設置年輕代的初始值和最大值)
-Xincgc :啓動增量垃圾收集器,缺省是關閉的。增量垃圾收集器能減少偶然發生的長時間的垃圾回收造成的暫停時間。但增量垃圾收集器和應用程序併發執行,因此會佔用部分CPU在應用程序上的功能。
-XX:CMSInitiatingOccupancyFraction=70 發現引起promotion failed錯誤的原因是CMS來不及回收(CMS默認在年老代佔到90%左右纔會執行),年老代又沒有足夠的空間供GC把一些活的對象從年輕代移到年老代,所以執行Full GC。CMSInitiatingOccupancyFraction=70表示年老代佔到約70%時就開始執行CMS,這樣就不會頻繁出現Full GC了。
上兩個參數設置有很大技巧,基本上滿足:
(Xmx-Xmn)*(100- CMSInitiatingOccupancyFraction)/100>=Xmn就不會出現promotion failed。如果在應用中設置Xmx(最大內存)是1500m,Xmn(年輕代)是340m,那麼Xmx-Xmn是1160m,也就是年老代有1160 兆,CMSInitiatingOccupancyFraction=70說明年老代到70%滿的時候開始執行對年老代的併發垃圾回收(CMS),這時還剩30%的空間是1160*30%=348兆,所以即使Xmn(也就是年輕代共340兆)裏所有對象都搬到年老代裏,348兆的空間也足夠了,所以只要滿足上面的公式,就不會出現垃圾回收時的promotionfailed
-XX:PermSize= xxxm:設定xxx兆內存的永久保存區域
-XX:MaxPermSize=xxxm:設定xxx兆最大內存的永久保存區域
PermSize和MaxPermSize指明虛擬機爲java永久生成對象(Permanate generation)如,class對象、方法對象這些可反射(reflective)對象分配內存限制,這些內存不包括在Heap(堆內存)區之中。上述參數如果不設定,永久保存區域默認大小:-server選項下默認MaxPermSize爲64m,-client選項下默認MaxPermSize爲32m。運行程序時,jvm會調整永久保存區域的大小以滿足需要。每次調整時,jvm會對堆進行一次完全的垃圾收集。
-XX:+UseConcMarkSweepGC :選擇CMS收集器(併發回收,縮短major收集的時間)
提示:此選項在Heap Size 比較大而且Major收集時間較長的情況下使用更合適。
【-XX:+UseParNewGC :對年輕代採用多線程並行回收,這樣收得快(縮短minor收集的時間,如果設置-XX:+UseConcMarkSweepGC,無須設置-XX:+UseParNewGC,是默認的)】
-XX:MaxTenuringThreshold=5 CMS收集器中,新生代對象撐過過多少次minor gc才進入年老代的。默認爲0(或另一說法:一個對象如果在救助空間移動5次還沒有被回收就放入年老代)。如果設置爲0就是去掉了新生代空間,存活的臨時對象不經過Survivor區直接進入年老代,不久就佔滿年老代發生full gc
-XX:GCTimeRatio=19 表示java可以用5%的時間來做垃圾回收,1/(1+19)=1 /20=5%
-XX:CMSFullGCsBeforeCompaction=N表示執行N次Full GC後執行內存壓縮,免得產生內存碎片
(案例都設置爲:-XX:CMSFullGCsBeforeCompaction=0)
-XX:+UseCMSCompactAtFullCollection:表示執行Full GC後對內存進行整理壓縮,免得產生內存碎片
-Xnoclassgc:禁用類垃圾回收,性能會高一點;
-verbose:gc 顯示垃圾收集信息 (在虛擬機發生內存回收時在輸出設備顯示信息)
-Xloggc:gc.log 指定垃圾收集日誌文件
-XX:+DisableExplicitGC禁止System.gc():免得程序員誤調用gc方法影響性能;
-XX:+ExplicitGCInvokesConcurrent:System.gc()可以與應用程序併發執行。
(System.gc()來收回不用的內存,是寫在程序裏的。System.gc()只是“建議”JVM回收內存,不是強制。禁止System.gc()要看實際開發的程序如何處理。因此編程要養成習慣,創建一個對象,不再用時指向null,這樣jvm發現它不再使用時,會更早的把它放進回收隊列,才能更早的進行回收。
例如:
你要銷燬一個對象,可以
代碼:
String a = "ksadjflasdf";
//do something.
//
a=null; 這不是銷燬一個對象 僅僅是把對一個對象的引用去掉了
在java中一個對象可以被多個對象引用的只有一個對象不在被引用時纔可以被垃圾收集)
-XX:SoftRefLRUPolicyMSPerMB=N 這個參數比較有用的,官方解釋是:Soft reference在虛擬機中比在客戶集中存活的更長一些。其清除頻率可以用命令行參數 -XX:SoftRefLRUPolicyMSPerMB=<N>來控制,這可以指定每兆堆空閒空間的 soft reference 保持存活(一旦它不強可達了)的毫秒數,這意味着每兆堆中的空閒空間中的 soft reference 會(在最後一個強引用被回收之後)存活1秒鐘。注意,這是一個近似的值,因爲 soft reference 只會在垃圾回收時纔會被清除,而垃圾回收並不總在發生。系統默認爲一秒,我覺得沒必要等1秒,客戶集中不用就立刻清除,改爲-XX:SoftRefLRUPolicyMSPerMB=0;
-Xss 15120 這使得JBoss每增加一個線程(thread)就會立即消耗15M內存,而最佳值應該是128K,默認值好像是512k.
+XX:AggressiveHeap 會使得 Xms沒有意義。這個參數讓jvm忽略Xmx參數,瘋狂地吃完一個G物理內存,再吃盡一個G的swap。
-Xss:每個線程的Stack大小
顯示日誌參數
-verbose:gc在虛擬機發生內存回收時在輸出設備顯示信息,格式如下:
[Full GC268K->168K(1984K), 0.0187390 secs]
該參數用來監視虛擬機內存回收的情況。-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps(GC發生的時間)
-XX:+PrintGCApplicationStoppedTime(GC消耗了多少時間)
-XX:+PrintGCApplicationConcurrentTime(GC之間運行了多少時間)
-XX:+PrintTenuringDistribution 參數觀察各個Age的對象總大小
2GC日誌打印
GC調優是個很實驗很伽利略的活兒,GC日誌是先決的數據參考和最終驗證:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps(GC發生的時間)-XX:+PrintGCApplicationStoppedTime(GC消耗了多少時間)-XX:+PrintGCApplicationConcurrentTime(GC之間運行了多少時間)
3 收集器選擇
配置參數:-XX:+UseConcMarkSweepGC
已默認無需配置的參數:-XX:+UseParNewGC(Parallel收集新生代)
-XX:+CMSPermGenSweepingEnabled(CMS收集持久代)-XX:UseCMSCompactAtFullCollection(full gc時壓縮年老代)
初始效果:1g堆內存的新生代約60m,minorgc約5-20毫秒,full gc約130毫秒。
配置參數: -XX:+UseParallelGC-XX:+UseParallelOldGC(Parallel收集年老代,從JDK6.0開始支持)
已默認無需配置的參數: -XX:+UseAdaptiveSizePolicy(動態調整新生代大小)
初始效果:1g堆內存的新生代約90-110m(動態調整),minor gc約5-20毫秒,fullgc有無UseParallelOldGC 參數分別爲1.3/1.1秒,差別不大。
另外-XX:MaxGCPauseMillis=100設置minor gc的期望最大時間,JVM會以此來調整新生代的大小,但在此測試環境中對象死的太快,此參數作用不大。
Parallel收集高達1秒的暫停時間基本不可忍受,所以選擇CMS收集器。
在被壓測的Mule 2.0應用裏,每秒都有大約400M的海量短命對象產生:
因爲默認60M的新生代太小了,頻繁發生minor gc,大約0.2秒就進行一次。
因爲CMS收集器中MaxTenuringThreshold(新生代對象撐過過多少次minor gc才進入年老代的設置)默認0,存活的臨時對象不經過Survivor區直接進入年老代,不久就佔滿年老代發生full gc。
對這兩個參數的調優,既要改善上面兩種情況,又要避免新生代過大,複製次數過多造成minorgc的暫停時間過長。
使用-Xmn調到1/3 總內存。觀察後設置-Xmn500M,新生代實際約460m。(用-XX:NewRatio設置無效,只能用 -Xmn)。
添加-XX:+PrintTenuringDistribution 參數觀察各個Age的對象總大小,觀察後設置-XX:MaxTenuringThreshold=5。
優化後,大約1.1秒才發生一次minorgc,且速度依然保持在15-20ms之間。同時年老代的增長速度大大減緩,很久才發生一次full gc,
參數定稿:
-server -Xms1536m -Xmx1536m-Xmn512m
-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=5 -XX:+ExplicitGCInvokesConcurrent-XX:GCTimeRatio=19 -XX:CMSInitiatingOccupancyFraction=70 -XX:CMSFullGCsBeforeCompaction=0 -Xnoclassg
最後服務處理速度從1180 tps 上升到1380 tps,調整兩個參數提升17%的性能還是筆很划算的買賣。
另外,JDK6 Update 7自帶了一個VisualVM工具,內裏就是之前也有用過的Netbean Profiler,類似JConsole一樣使用,可以看到線程狀態,內存中對象以及方法的CPU時間等調優重要參考依據。免費捆綁啊,Sun 這樣搞法,其他做Profiler的公司要關門了。
標準參數設置參考樣本(tomcat可用8G內存案例)
set JAVA_OPTS=%JAVA_OPTS% -server –Xms8192m –Xmx8192m–Xmn1890m -verbose:gc -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=5 -XX:+ExplicitGCInvokesConcurrent -XX:GCTimeRatio=19 -XX:CMSInitiatingOccupancyFraction=70-XX:CMSFullGCsBeforeCompaction=0 –Xnoclassgc -XX:SoftRefLRUPolicyMSPerMB=0