Tomcat調優和JVM優化
Tomcat本身優化
工作方式選擇
為了提升效能,首先就要對程式碼進行動靜分離,讓 Tomcat 只負責 jsp 檔案的解析工作。如採用 Apache 和 Tomcat 的整合方式,他們之間的連線方案有三種選擇,JK、http_proxy 和 ajp_proxy。相對於 JK 的連線方式,後兩種在配置上比較簡單的,靈活性方面也一點都不遜色。但就穩定性而言不像JK 這樣久經考驗,所以建議採用 JK 的連線方式。
Connector 聯結器的配置
之前檔案介紹過的 Tomcat 聯結器的三種方式: bio、nio 和 apr,三種方式效能差別很大,apr 的效能最優, bio 的效能最差。而 Tomcat 7 使用的 Connector 預設就啟用的 Apr 協議,但需要系統安裝 Apr 庫,否則就會使用 bio 方式。
配置檔案設定
常見異常-java.lang.OutOfMemoryError: Java heap space
JVM堆的設定是指java程式執行過程中JVM可以調配使用的記憶體空間的設定.JVM在啟動的時候會自動設定Heap size的值,其初始空間(即-Xms)是實體記憶體的1/64,最大空間(-Xmx)是實體記憶體的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等選項可進行設定。Heap size 的大小是Young Generation 和Tenured Generaion 之和。
在JVM中如果98%的時間是用於GC且可用的Heap size 不足2%的時候將丟擲此異常資訊
。Heap Size 最大不要超過可用實體記憶體的80%,一般的要將-Xms和-Xmx選項設定為相同,而-Xmn為1/4的-Xmx值。
通常情況下,這種問題出現在實際的生產環境中.產生這種問題的原因是tomcat使用較少的記憶體給程序,通過配置TOmcat的配置檔案(Windows 下的catalina.bat或Linux下的catalina.sh)可以解決這種問題.這種解決方法是通過增加JVM的棧記憶體實現的.也就是說,JVM通常不去呼叫垃圾回收器,所以伺服器可以更多關注處理web請求,並要求儘快完成。要更改檔案(catalina.sh) 位於
"\tomcat server folder\bin\catalina.sh"
Linux下修改JVM記憶體大小:
要新增在tomcat 的bin 下catalina.sh 裡,位置cygwin=false前 。
# OS specific support. $var _must_ be set to either true or false.
JAVA_OPTS="-Xms256m -Xmx512m -Xss1024K -XX:PermSize=128m -XX:MaxPermSize=256m -XX:+UseConcMarkSweepGC\
-XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=500\
-XX:+CMSClassUnloadingEnabled -XX:+CMSClassUnloadingEnabled -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
cygwin=false
引數解釋
-server:tomcat預設是以一種叫java –client的模式來執行的,server即意味著你的tomcat是以真實的production的模式在執行的效能更優
-Xms–Xmx:JVM記憶體設定,JVM初始分配的堆記憶體由-Xms指定,預設是實體記憶體的1/64;JVM最大分配的堆記憶體由-Xmx指定,預設是實體記憶體的1/4。預設空餘堆記憶體小於40%時,JVM就會增大堆直到-Xmx的最大限制;
空餘堆記憶體大於70%時,JVM會減少堆直到-Xms的最小限制,建議把最大和最小設定成一樣有利於JVM的垃圾回收機制
–Xmn:設定新生代,整個堆大小=新生代大小 + 年老代大小 + 持久代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8
-XX:每當JDK版本升級時,你的JVM都會使用最新加入的優化技術
-XX:PermSize:設定非堆記憶體初始值,預設是實體記憶體的1/64
-XX:MaxPermSize:設定永生代記憶體初始大小,即最大非堆記憶體的大小,預設是實體記憶體的1/4
-XX:+UseConcMarkSweepGC:CMS gc,這一特性只有jdk1.5即後續版本才具有的功能,它使用的是gc估算觸發和heap佔用觸發
-XX:+UseCMSCompactAtFullCollection:在使用concurrent gc 的情況下, 防止 memoryfragmention, 對live object 進行整理, 使 memory 碎片減少
-XX:+UseParNewGC:對新生代採用多執行緒並行回收,這樣收得快
-XX:+CMSClassUnloadingEnabled:CMS收集器預設不會對永久代進行垃圾回收
-XX:CMSMaxAbortablePrecleanTime:CMS GC需要經過較多步驟才能完成一次GC的動作,在minor GC較為頻繁的情況下,很有可能造成CMS GC尚未完成,從而造成concurrent mode failure,可以通過-XX: CMSMaxAbortablePrecleanTime設定較小的值,以保證CMS GC儘快完成物件的回收,避免concurrent mode failure的現象,尤其是在JDK 5.0+、6.0+的有些版本在CMS-concurrent-abortable-preclean-start和CMS-concurrent-abortable-preclean這兩步間有可能會耗費很長的時間,導致可回收的舊生代的物件很長時間後才被回收,這是Sun JDK CMS GC的一個bug
在重啟你的Tomcat伺服器之後,這些配置的更改才會有效。
#### 常見異常-java.lang.OutOfMemoryError: PermGen space
PermGen space 的全稱是 Permanent Generation space,是指記憶體的永久儲存區域。為什麼會記憶體溢位,這是由於這塊記憶體主要是被 JVM 存放Class 和 Meta 資訊的,Class 在被 Load 的時候被放入 PermGen space 區域,它和存放 Instance 的 Heap 區域不同,sun 的 GC 不會在主程式執行期對 PermGen space 進行清理,所以如果你的 APP 會載入很多 CLASS 的話,就很可能出現 PermGen space 溢位。
解決方法: 手動設定 MaxPermSize 大小
常見異常-java.lang.StackOverflowError
棧溢位了,JVM 依然是採用棧式的虛擬機器,這個和 C 與 Pascal 都是一樣的。函式的呼叫過程都體現在堆疊和退棧上了。呼叫建構函式的 “層”太多了,以致於把棧區溢位了。通常來講,一般棧區遠遠小於堆區的,因為函式呼叫過程往往不會多於上千層,而即便每個函式呼叫需要 1K 的空間(這個大約相當於在一個 C 函式內聲明瞭 256 個 int 型別的變數),那麼棧區也不過是需要 1MB 的空間。通常棧的大小是 1-2MB 的。
通常遞迴也不要遞迴的層次過多,很容易溢位。
解決方法:修改程式。
執行緒池設定
執行緒池指定Web請求負載的數量,因此,為獲得更好的效能這部分應小心處理。可以通過調整聯結器屬性“maxThreads”完成設定。maxThreads的值應該根據流量的大小,如果值過低,將有沒有足夠的執行緒來處理所有的請求,請求將進入等待狀態,只有當一個的處理執行緒釋放後才被處理;如果設定的太大,Tomcat的啟動將花費更多時間。因此它取決於我們給maxThreads設定一個正確的值。
Tomcat的server.xml配置檔案
<Connector port="8080"address="localhost"
maxThreads="1000"maxHttpHeaderSize="8192"
minSpareThreads="100"
maxProcessors="1200" acceptCount="1000"
emptySessionPath="true"protocol="HTTP/1.1"
enableLookups="false"redirectPort="8181"acceptCount="100"
connectionTimeout="20000"disableUploadTimeout="true"/>
在上述配置中,maxThreads值設定為“250”,這指定可以由伺服器處理的併發請求的最大數量。如果沒有指定,這個屬性的預設值為“200”。
connectionTimeout:超時時間,單位毫秒,預設值為20000
maxThreads:tomcat:起動的最大執行緒數,預設值為250
minSpareThreads:Tomcat初始化時建立的執行緒數。預設值4
maxProcessors:Tomcat執行時允許建立的最大執行緒數,預設值為75,一般根據實際生產環境修改
acceptCount:當tomcat起動的執行緒數達到最大時,接受排隊的請求個數,預設值為100,web server允許的最大連線數還受制於作業系統的核心引數設定,通常Windows是2000個左右,Linux是1000個左右,通常該值設定同maxThreads一樣
enableLookups:是否反查域名,預設值為true。為了提高處理能力,應設定為false
compression:壓縮傳輸,取值on/off/force,預設值off
redirectPort:SSL的重定向埠,預設8443
Tomcat壓縮
Tomcat有一個通過在server.xml配置檔案中設定壓縮的選項。壓縮可以在connector像如下設定中完成,
server.xml
<Connector port="8080"protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8181"compression="500"
compressableMimeType="text/html,text/xml,text/plain,application/octet-stream"/>
在前面的配置中,當檔案的大小大於等於500bytes時才會壓縮。如果當檔案達到了大小但是卻沒有被壓縮,那麼設定屬性compression=”on”。否則Tomcat預設設定是“off”。接下來我們將看看如何調優資料庫。
JVM 常用引數詳解:
-server:一定要作為第一個引數,在多個 CPU 時效能佳,還有一種叫 -client 的模式,特點是啟動速度比較快,但執行時效能和記憶體管理效率不高,通常用於客戶端應用程式或開發除錯,在 32 位環境下直接執行 Java 程式預設啟用該模式。Server 模式的特點是啟動速度比較慢,但執行時效能和記憶體管理效率很高,適用於生產環境,在具有 64 位能力的 JDK 環境下預設啟用該模式,可以不配置該引數。
-Xms:表示 Java 初始化堆的大小,-Xms 與-Xmx 設成一樣的值,避免 JVM 反覆重新申請記憶體,導致效能大起大落,預設值為實體記憶體的 1/64,預設(MinHeapFreeRatio引數可以調整)空餘堆記憶體小於 40% 時,JVM 就會增大堆直到 -Xmx 的最大限制。
-Xmx:表示最大 Java 堆大小,當應用程式需要的記憶體超出堆的最大值時虛擬機器就會提示記憶體溢位,並且導致應用服務崩潰,因此一般建議堆的最大值設定為可用記憶體的最大值的80%。如何知道我的 JVM 能夠使用最大值,使用 java -Xmx512M -version 命令來進行測試,然後逐漸的增大 512 的值,如果執行正常就表示指定的記憶體大小可用,否則會列印錯誤資訊,預設值為實體記憶體的 1/4,預設(MinHeapFreeRatio引數可以調整)空餘堆記憶體大於 70% 時,JVM 會減少堆直到-Xms 的最小限制。
-Xss:表示每個 Java 執行緒堆疊大小,JDK 5.0 以後每個執行緒堆疊大小為 1M,以前每個執行緒堆疊大小為 256K。根據應用的執行緒所需記憶體大小進行調整,在相同實體記憶體下,減小這個值能生成更多的執行緒,但是作業系統對一個程序內的執行緒數還是有限制的,不能無限生成,經驗值在 3000~5000 左右。一般小的應用, 如果棧不是很深, 應該是128k 夠用的,大的應用建議使用 256k 或 512K,一般不易設定超過 1M,要不然容易出現out ofmemory。這個選項對效能影響比較大,需要嚴格的測試。
-XX:NewSize:設定新生代記憶體大小。
-XX:MaxNewSize:設定最大新生代新生代記憶體大小
-XX:PermSize:設定持久代記憶體大小
-XX:MaxPermSize:設定最大值持久代記憶體大小,永久代不屬於堆記憶體,堆記憶體只包含新生代和老年代。
-XX:+AggressiveOpts:作用如其名(aggressive),啟用這個引數,則每當 JDK 版本升級時,你的 JVM 都會使用最新加入的優化技術(如果有的話)。
-XX:+UseBiasedLocking:啟用一個優化了的執行緒鎖,我們知道在我們的appserver,每個http請求就是一個執行緒,有的請求短有的請求長,就會有請求排隊的現象,甚至還會出現執行緒阻塞,這個優化了的執行緒鎖使得你的appserver內對執行緒處理自動進行最優調配。
-XX:+DisableExplicitGC:在 程式程式碼中不允許有顯示的呼叫“System.gc()”。每次在到操作結束時手動呼叫 System.gc() 一下,付出的代價就是系統響應時間嚴重降低,就和關於 Xms,Xmx 裡的解釋的原理一樣,這樣去呼叫 GC 導致系統的 JVM 大起大落。
-XX:+UseConcMarkSweepGC:設定年老代為併發收集,即 CMS gc,這一特性只有 jdk1.5
後續版本才具有的功能,它使用的是 gc 估算觸發和 heap 佔用觸發。我們知道頻頻繁的 GC 會造面 JVM
的大起大落從而影響到系統的效率,因此使用了 CMS GC 後可以在 GC 次數增多的情況下,每次 GC 的響應時間卻很短,比如說使用了 CMS
GC 後經過 jprofiler 的觀察,GC 被觸發次數非常多,而每次 GC 耗時僅為幾毫秒。
-XX:+UseParNewGC:對新生代採用多執行緒並行回收,這樣收得快,注意最新的 JVM 版本,當使用 -XX:+UseConcMarkSweepGC 時,-XX:UseParNewGC 會自動開啟。因此,如果年輕代的並行 GC 不想開啟,可以通過設定 -XX:-UseParNewGC 來關掉。
-XX:MaxTenuringThreshold:設定垃圾最大年齡。如果設定為0的話,則新生代物件不經過 Survivor 區,直接進入老年代。對於老年代比較多的應用(需要大量常駐記憶體的應用),可以提高效率。如果將此值設定為一 個較大值,則新生代物件會在 Survivor 區進行多次複製,這樣可以增加物件在新生代的存活時間,增加在新生代即被回收的概率,減少Full GC的頻率,這樣做可以在某種程度上提高服務穩定性。該引數只有在序列 GC 時才有效,這個值的設定是根據本地的 jprofiler 監控後得到的一個理想的值,不能一概而論原搬照抄。
-XX:+CMSParallelRemarkEnabled:在使用 UseParNewGC 的情況下,儘量減少 mark 的時間。
-XX:+UseCMSCompactAtFullCollection:在使用 concurrent gc 的情況下,防止 memoryfragmention,對 live object 進行整理,使 memory 碎片減少。
-XX:LargePageSizeInBytes:指定 Java heap 的分頁頁面大小,記憶體頁的大小不可設定過大, 會影響 Perm 的大小。
-XX:+UseFastAccessorMethods:使用 get,set 方法轉成原生代碼,原始型別的快速優化。
-XX:+UseCMSInitiatingOccupancyOnly:只有在 oldgeneration 在使用了初始化的比例後 concurrent collector 啟動收集。
-Duser.timezone=Asia/Shanghai:設定使用者所在時區。
-Djava.awt.headless=true:這個引數一般我們都是放在最後使用的,這全引數的作用是這樣的,有時我們會在我們的 J2EE 工程中使用一些圖表工具如:jfreechart,用於在 web 網頁輸出 GIF/JPG 等流,在 winodws 環境下,一般我們的 app server 在輸出圖形時不會碰到什麼問題,但是在linux/unix 環境下經常會碰到一個 exception 導致你在 winodws 開發環境下圖片顯示的好好可是在 linux/unix 下卻顯示不出來,因此加上這個引數以免避這樣的情況出現。
-Xmn:新生代的記憶體空間大小,注意:此處的大小是(eden+ 2 survivor space)。與 jmap -heap 中顯示的 New gen 是不同的。整個堆大小 = 新生代大小 + 老生代大小 + 永久代大小。在保證堆大小不變的情況下,增大新生代後,將會減小老生代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的 3/8。
-XX:CMSInitiatingOccupancyFraction:當堆滿之後,並行收集器便開始進行垃圾收集,例如,當沒有足夠的空間來容納新分配或提升的物件。對於 CMS 收集器,長時間等待是不可取的,因為在併發垃圾收集期間應用持續在執行(並且分配物件)。因此,為了在應用程式使用完記憶體之前完成垃圾收集週期,CMS 收集器要比並行收集器更先啟動。因為不同的應用會有不同物件分配模式,JVM 會收集實際的物件分配(和釋放)的執行時資料,並且分析這些資料,來決定什麼時候啟動一次 CMS 垃圾收集週期。這個引數設定有很大技巧,基本上滿足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100 >= Xmn 就不會出現 promotion failed。例如在應用中 Xmx 是6000,Xmn 是 512,那麼 Xmx-Xmn 是 5488M,也就是老年代有 5488M,CMSInitiatingOccupancyFraction=90 說明老年代到 90% 滿的時候開始執行對老年代的併發垃圾回收(CMS),這時還 剩 10% 的空間是 5488*10% = 548M,所以即使 Xmn(也就是新生代共512M)裡所有物件都搬到老年代裡,548M 的空間也足夠了,所以只要滿足上面的公式,就不會出現垃圾回收時的 promotion failed,因此這個引數的設定必須與 Xmn 關聯在一起。
-XX:+CMSIncrementalMode:該標誌將開啟 CMS 收集器的增量模式。增量模式經常暫停 CMS 過程,以便對應用程式執行緒作出完全的讓步。因此,收集器將花更長的時間完成整個收集週期。因此,只有通過測試後發現正常 CMS 週期對應用程式執行緒干擾太大時,才應該使用增量模式。由於現代伺服器有足夠的處理器來適應併發的垃圾收集,所以這種情況發生得很少,用於但 CPU情況。
-XX:NewRatio:年輕代(包括 Eden 和兩個 Survivor 區)與年老代的比值(除去持久代),-XX:NewRatio=4 表示年輕代與年老代所佔比值為 1:4,年輕代佔整個堆疊的 1/5,Xms=Xmx 並且設定了 Xmn 的情況下,該引數不需要進行設定。
-XX:SurvivorRatio:Eden 區與 Survivor 區的大小比值,設定為 8,表示 2 個 Survivor 區(JVM 堆記憶體年輕代中預設有 2 個大小相等的 Survivor 區)與 1 個 Eden 區的比值為 2:8,即 1 個 Survivor 區佔整個年輕代大小的 1/10。
-XX:+UseSerialGC:設定序列收集器。
-XX:+UseParallelGC:設定為並行收集器。此配置僅對年輕代有效。即年輕代使用並行收集,而年老代仍使用序列收集。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式為並行收集,JDK6.0 開始支援對年老代並行收集。
-XX:ConcGCThreads:早期 JVM 版本也叫-XX:ParallelCMSThreads,定義併發 CMS 過程執行時的執行緒數。比如 value=4 意味著 CMS 週期的所有階段都以 4 個執行緒來執行。儘管更多的執行緒會加快併發 CMS 過程,但其也會帶來額外的同步開銷。因此,對於特定的應用程式,應該通過測試來判斷增加 CMS 執行緒數是否真的能夠帶來效能的提升。如果還標誌未設定,JVM 會根據並行收集器中的 -XX:ParallelGCThreads 引數的值來計算出預設的並行 CMS 執行緒數。
-XX:ParallelGCThreads:配置並行收集器的執行緒數,即:同時有多少個執行緒一起進行垃圾回收,此值建議配置與 CPU 數目相等。
-XX:OldSize:設定 JVM 啟動分配的老年代記憶體大小,類似於新生代記憶體的初始大小 -XX:NewSize。