核心部分
- 內存
- 線程
- IO
- 壓縮
- 緩存
- 集群
一、JVM內存優化
Tomcat內存優化,包括內存大小,垃圾回收策略。
Windows 下的catalina.bat,Linux 下的catalina.sh
系統響應時間增快;
JVM回收速度增快同時又不影響系統的響應率;
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。
二、Tomcat線程調優
在server.xml中, 包括線程調優,取消DNS,壓縮。
maxThreads :Tomcat 使用線程來處理接收的每個請求,這個值表示 Tomcat 可創建的最大的線程數,默認值是 200
minSpareThreads:最小空閑線程數,Tomcat 啟動時的初始化的線程數,表示即使沒有人使用也開這么多空線程等待,默認值是 10。
maxSpareThreads:最大備用線程數,一旦創建的線程超過這個值,Tomcat 就會關閉不再需要的 socket 線程。
上邊配置的參數,最大線程 500(一般服務器足以),要根據自己的實際情況合理設置,設置越大會耗費內存和 CPU,因為 CPU 疲於線程上下文切換,沒有精力提供請求服務了,最小空閑線程數 20,線程最大空閑時間 60 秒,當然允許的最大線程連接數還受制於操作系統的內核參數設置,設置多大要根據自己的需求與環境。當然線程可以配置在“tomcatThreadPool”中,也可以直接配置在“Connector”中,但不可以重復配置。
URIEncoding:指定 Tomcat 容器的 URL 編碼格式,語言編碼格式這塊倒不如其它 WEB 服務器軟件配置方便,需要分別指定。
connnectionTimeout: 網絡連接超時,單位:毫秒,設置為 0 表示永不超時,這樣設置有隱患的。通常可設置為 30000 毫秒,可根據檢測實際情況,適當修改。
enableLookups: 是否反查域名,以返回遠程主機的主機名,取值為:true 或 false,如果設置為false,則直接返回IP地址,為了提高處理能力,應設置為 false。
disableUploadTimeout:上傳時是否使用超時機制。
connectionUploadTimeout:上傳超時時間,畢竟文件上傳可能需要消耗更多的時間,這個根據你自己的業務需要自己調,以使Servlet有較長的時間來完成它的執行,需要與上一個參數一起配合使用才會生效。
acceptCount:指定當所有可以使用的處理請求的線程數都被使用時,可傳入連接請求的最大隊列長度,超過這個數的請求將不予處理,默認為100個。
keepAliveTimeout:長連接最大保持時間(毫秒),表示在下次請求過來之前,Tomcat 保持該連接多久,默認是使用 connectionTimeout 時間,-1 為不限制超時。
maxKeepAliveRequests:表示在服務器關閉之前,該連接最大支持的請求數。超過該請求數的連接也將被關閉,1表示禁用,-1表示不限制個數,默認100個,一般設置在100~200之間。
compression:是否對響應的數據進行 GZIP 壓縮,off:表示禁止壓縮;on:表示允許壓縮(文本將被壓縮)、force:表示所有情況下都進行壓縮,默認值為off,壓縮數據后可以有效的減少頁面的大小,一般可以減小1/3左右,節省帶寬。
compressionMinSize:表示壓縮響應的最小值,只有當響應報文大小大於這個值的時候才會對報文進行壓縮,如果開啟了壓縮功能,默認值就是2048。
compressableMimeType:壓縮類型,指定對哪些類型的文件進行數據壓縮。
noCompressionUserAgents="gozilla, traviata": 對於以下的瀏覽器,不啟用壓縮。
如果已經對代碼進行了動靜分離,靜態頁面和圖片等數據就不需要 Tomcat 處理了,那么也就不需要配置在 Tomcat 中配置壓縮了。
三、Tomcat IO 調優
Tomcat 連接器的三種方式: bio、nio 和 apr,三種方式性能差別很大,apr 的性能最優, bio 的性能最差。而 Tomcat 7 使用的 Connector 默認就啟用的 Apr 協議,但需要系統安裝 Apr 庫,否則就會使用 bio 方式。
1:同步阻塞IO(JAVA BIO) 同步並阻塞,服務器實現模式為一個連接一個線程(one connection one thread 想想都覺得恐怖,線程可是非常寶貴的資源),當然可以通過線程池機制改善.
2:JAVA NIO:又分為同步非阻塞IO,異步阻塞IO 與BIO最大的區別one request one thread.可以復用同一個線程處理多個connection(多路復用).
3:,異步非阻塞IO(Java NIO2又叫AIO) 主要與NIO的區別主要是操作系統的底層區別.可以做個比喻:比作快遞,NIO就是網購后要自己到官網查下快遞是否已經到了(可能是多次),然后自己去取快遞;AIO就是快遞員送貨上門了(不用關注快遞進度)。
BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,並發局限於應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解.
NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局限於應用中,編程比較復雜,JDK1.4開始支持.
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與並發操作,編程比較復雜,JDK7開始支持.
APR是從操作系統級別來解決異步的IO問題,大幅度的提高性能. (http://apr.apache.org/).
四、緩存和壓縮
對於靜態頁面最好是能夠緩存起來,這樣就不必每次從磁盤上讀。這里我們采用了Nginx作為緩存服務器,將圖片、css、js文件都進行了緩存,有效的減少了后端tomcat的訪問。另外,為了能加快網絡傳輸速度,開啟gzip壓縮也是必不可少的。但考慮到tomcat已經需要處理很多東西了,所以把這個壓縮的工作就交給前端的Nginx來完成。除了文本可以用gzip壓縮,其實很多圖片也可以用圖像處理工具預先進行壓縮,找到一個平衡點可以讓畫質損失很小而文件可以減小很多。曾經我就見過一個圖片從300多kb壓縮到幾十kb,自己幾乎看不出來區別。
五、集群
單個服務器性能總是有限的,最好的辦法自然是實現橫向擴展,那么組建tomcat集群是有效提升性能的手段。我們還是采用了Nginx來作為請求分流的服務器,后端多個tomcat共享session來協同工作。
摘錄地址
- 【面試總結】--tomcat調優方案
- Java面試題Tomcat的優化經驗
- 閑談Tomcat性能優化
- Tomcat安裝、配置、優化及負載均衡詳解
- Tomcat學習總結(8)—— Tomcat常見面試題