Java Web之Tomcat調優

YungFan發表於2017-12-13

Tomcat調優是一個老話題,目的都是為了提高站點的吞吐和併發。這裡面涉及到Tomcat本身引數的優化和JVM優化。近期在研究JVM的引數設定和Tomcat叢集,所以進行了一下調優實踐。需要說明的是:本文的配置肯定不是最好的,僅僅是一次實踐和一次記錄。步驟可以參考,但引數設定需要針對不同需求的專案來進行調整。

1、安裝APR(Apache Portable Runtime)

Tomcat 7 以後 Connector 預設啟用 APR 協議,但是隻有配置了 APR庫才可以生效,否則還是會使用 BIO 或者NIO方式。如何配置?可以直接去http://tomcat.apache.org/download-native.cgi下載,裡面有32bit和64bit兩種庫,本人是64位機器,所以將x64資料夾下的兩個檔案tcnative-1.dlltcnative-1-src.pdb拷貝到Tomcat的bin目錄下,然後啟動 Tomcat,如果有如圖所示的輸出,說明配置成功。

安裝Apr後啟動狀態變化.PNG

為什麼使用Apr?

官方如是說(翻譯了一下):Tomcat 可以使用 APR 來提供出色的可伸縮性,效能以及與原生伺服器技術的更好整合。APR 有許多用途,提供了包括對高階IO功能作業系統級功能和本地程式處理的訪問。這些功能使得Tomcat成為一個通用的網路伺服器,能夠更好地與其他本地網路技術進行整合,並且使整個Java作為一個完整的web伺服器平臺更加可行。

網上有人測試過,APR相比較BIO、NIO,在處理高併發時效率更高。

2、配置Executor,增加執行緒數,提高併發能力

開啟server.xml,找到被註釋的一段

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
            maxThreads="150" minSpareThreads="4"/>
複製程式碼

修改成

    <Executor 	        name="tomcatThreadPool" 
                        namePrefix="catalina-exec-"
       		 	maxThreads="500" 		//Tomcat 使用執行緒來處理請求,該值表示 Tomcat 能建立的最大的執行緒數
       		 	minSpareThreads="20"            //最小空閒執行緒數,Tomcat 啟動時的初始化的執行緒數,表示即使沒有人使用也開這麼多空執行緒等待,預設值是 10。
       		 	maxIdleTime="60000"/>           //當服務的執行緒數超過最大執行緒數時,超過maxIdleTime的執行緒會被殺死回收直到執行緒數降低到最大數以內

複製程式碼

3、配置Connector,設定超時、上傳和快取資訊

同樣在server.xml中,修改埠號為 8080 的Connector如下

    <Connector      executor="tomcatThreadPool" 	//指明上面的executor
            	    port="8080" 
                    protocol="HTTP/1.1"
            	    URIEncoding="UTF-8"
            	    connectionTimeout="30000" 		//網路連線超時,設定為 0 表示永不超時。單位:毫秒
	            enableLookups="false"		//是否反查域名,以返回遠端主機的主機名,取值為:truefalse,如果設定為false,則直接返回IP地址,為了提高處理能力,應設定為 false
	            disableUploadTimeout="false"	//上傳時是否使用超時機制
	            connectionUploadTimeout="150000"    //上傳超時時間
	            acceptCount="300"	        	//指定當所有可以使用的處理請求的執行緒數都被使用時,可傳入連線請求的最大佇列長度,超過這個數的請求將不予處理
	            keepAliveTimeout="120000"		//長連線最大保持時間,表示在下次請求過來之前,Tomcat 保持該連線多久,-1 為不限制超時。單位:毫秒
	            maxKeepAliveRequests="1"		//表示在伺服器關閉之前,該連線最大支援的請求數。超過該請求數的連線也將被關閉,1表示禁用,-1表示不限制個數
	            compression="on"			//是否對響應的資料進行 GZIP 壓縮,off:表示禁止壓縮;on:表示允許壓縮(文字將被壓縮)、force:表示所有情況下都進行壓縮,預設值為off
	            compressionMinSize="2048"		//表示壓縮響應的最小值,只有當響應報文大小大於這個值的時候才會對報文進行壓縮,如果開啟了壓縮功能
	            compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png" //壓縮型別,指定對哪些型別的檔案進行資料壓縮。
	            redirectPort="8443" />

複製程式碼

4、開啟catalina.bat,開啟JAVA_OPTS 進行JVM設定

如何設定JVM?
1、整合開發環境下啟動並使用JVM,如eclipse需要修改根目錄檔案eclipse.ini;
2、Windows伺服器下安裝版Tomcat,可使用Tomcatw.exe工具(Tomcat目錄下)和直接修改登錄檔兩種方式修改JVM引數;
3、Windows伺服器解壓版Tomcat註冊Windows服務,方法同上;
4、解壓版本的Tomcat, 通過startup.bat啟動Tomcat載入配置的,在Tomcat的bin 下catalina.bat 檔案內新增;
5、Linux伺服器Tomcat設定JVM,修改TOMCAT_HOME/bin/catalina.sh;

本人屬於第4種,所以開啟catalina.bat,在第二行新增如下資訊

set JAVA_OPTS=-server -Xms4G -Xmx4G -Xss512k
複製程式碼

5、檢測設定是否生效

在設定之前,訪問Tomcat首頁,點選Server Status檢視相關資訊

未優化之前的資訊.png

優化以後,再次訪問檢視,很明顯發生了很大變化

優化之後的資訊.png

參考文獻

1、http://blog.csdn.net/centre10/article/details/50639693 2、http://blog.csdn.net/ldx891113/article/details/51735171

附錄: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% 的空間是 548810% = 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。

相關文章