Tomcat 配置及優化

Tomcat配置優化,主要在於優化tomcat運行模式,併發參數和線程數, 以及jvm堆內存和垃圾回收相關參數的優化.下面將逐一介紹.javascript

1. tomcat的3種運行模式

1.1 BIO - 同步阻塞IO模式

BIO: 同步阻塞IO, 性能低, 沒有通過任何優化處理和支持.
服務器實現模式爲一個鏈接一個線程. 即, 客戶端有鏈接請求時, 服務器端就須要啓動一個線程進行處理. 若是這個鏈接不作任何事情會形成沒必要要的線程開銷, 固然能夠經過 線程池 機制改善.
適用場景: BIO方式適用於鏈接數比較小且固定的架構, 這種方式對服務器資源要求比較高, 有併發侷限, JDK1.4以前的惟一選擇.css

1.2 NIO - 同步非阻塞IO模式

NIO 是 Java SE 1.4 及後續版本提供的一種新的IO操做方式(即java.nio包及其子包).
Java NIO是一個基於緩衝區、並能提供非阻塞IO操做的Java API, 所以NIO也被當作是non-blocking IO(非阻塞式IO)的縮寫, 它擁有比傳統BIO操做更好的併發性能.
服務器實現模式爲一個請求一個線程, 即客戶端發送的鏈接請求都會註冊到多路複用器上, 多路複用器輪詢到鏈接有IO請求時才啓動一個線程進行處理.html

適用場景: 適用於鏈接數較多且鏈接比較時間短(輕操做)的架構, 好比聊天服務器. 這種方式的併發性能侷限於應用中, 編程比較複雜.
Tomcat 8.x默認運行在NIO模式下.java

1.3 APR - 可移植運行時模式

APR(Apache Portable Runtime, Apache可移植運行時), 是Apache HTTP服務器的一個支持庫, 它提供了一組映射到底層操做系統的API, 若是操做系統不支持特定功能, APR庫將提供仿真. 所以開發人員可使用APR使程序真正跨平臺移植.
此模式的安裝步驟比較繁瑣, 但卻從操做系統層面解決了異步IO的問題, 能大幅度提升應用性能.
APR的本質是使用 JNI 技術調用操做系統底層的IO接口, 因此須要提早安裝必要的依賴, 具體配置方法見下文描述.web

  BIO NIO NIO2 APR
類名 Http11Protocol Http11NioProtocol Http11Nio2Protocol Http11AprProtocol
引用版本 ≥3.0 ≥6.0 ≥8.0 ≥5.5
輪詢支持  
輪詢隊列大小 N/A maxConnections maxConnections maxConnections
讀請求頭 阻塞 非阻塞 非阻塞 阻塞
讀請求體 阻塞 阻塞 阻塞 阻塞
寫響應 阻塞 阻塞 阻塞 阻塞
等待新請求 阻塞 非阻塞 非阻塞 非阻塞
SSL支持 Java SSL Java SSL Java SSL Open SSL
SSL握手 阻塞 非阻塞 非阻塞 阻塞
最大連接數 maxConnections maxConnections maxConnections maxConnections

推薦使用nio,在tomcat8中有最新的nio2,速度更快,建議使用nio2apache

設置nio2:編程

<Connector executor="tomcatThreadPool"  port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               connectionTimeout="20000"
               redirectPort="8443" />

2. server.xml相關配置

2.1 Tomcat併發配置(connector配置)

Tomcat的Connector是其接收HTTP請求的關鍵模塊, 能夠經過它來指定IO處理模式, 指定處理該Connector接收到的請求的線程數, 以及其餘經常使用的HTTP策略.
配置路徑: 在 ${TOMCAT_HOME}/conf/server.xml 文件的節點中進行配置.數組

2.1.1 使用線程池處理請求

使用線程池, 經過較少的線程資源來處理更多的請求, 從而提升Tomcat的請求處理能力.
前提: 要提早配置至少一個線程池來處理請求, 配置文件爲${TOMCAT_HOME}/conf/server.xml.
其中Executor與Connector同級, 多個Connector可使用同一個線程池來處理請求.瀏覽器

1) 參考默認鏈接池配置:緩存

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
          maxThreads="150" minSpareThreads="4"/>

2) 自定義線程池示例:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" 
          maxThreads="200" minSpareThreads="10" maxIdleTime="600000" 
          prestartminSpareThreads="true" maxQueueSize="100"  /> 

3) 線程池參數說明:

name: 線程池名稱.
namePrefix: 建立的每一個線程的名稱前綴, 單獨的線程名稱爲 namePrefix + threadNumber.
maxThreads: 線程池中最大併發線程數, 默認值爲200, 通常建議設置400~ 800 , 要根據服務器配置和業務需求而定. minSpareThreads: 最小活躍線程數, 也就是核心線程數, 不會被銷燬, 會一直存在. prestartminSpareThreads: 是否在啓動程序時就生成minSpareThreads個線程, 默認爲false, 即不啓動. 若不設置爲true, 則minSpareThreads的設置就不起做用了. maxIdleTime: 線程最大空閒時間, 超過該時間後, 空閒線程會被銷燬, 默認值爲6000, 單位爲毫秒.
maxQueueSize: 最大的等待隊列數, 超過則拒絕請求. 默認值爲int類型的最大值(Integer.MAX_VALUE), 等同於無限大. 通常不做修改, 避免發生部分請求未能被處理的狀況. threadPriority: 線程池中線程的優先級, 默認值爲5, 取值範圍: 1 ~ 10.
className:線程池的實現類, 未指定狀況下, 默認實現類爲 org.apache.catalina.core.StandardThreadExecutor. 要自定義線程池就須要實現 org.apache.catalina.Executor 接口.

2.1.2 在Connector中使用線程池

Connector是Tomcat接收請求的入口, 每一個Connector都有本身專屬的監聽端口.

<Connector executor="tomcatThreadPool" 
             port="8080" protocol="HTTP/1.1" 
             connectionTimeout="20000" 
             redirectPort="8443" />

1) Connector的參數說明:

redirectPort="8443" # 基於SSL的端口, 在須要基於安全通道的場合, 好比當客戶端的請求協議是HTTPS時, 將該請求轉發到此端口.
minSpareThreads="25" # Tomcat鏈接器的最小空閒Socket線程數, 默認值爲25. 若是當前沒有空閒線程, 且沒有超過maxThreads, 將一次性建立的空閒線程數量. Tomcat初始化時建立的線程數量也是此值.
maxSpareThreads="75" # 最大空閒線程數, 一旦建立的線程超過此值, Tomcat就會關閉再也不須要的Socket線程, 默認值爲50. 線程數能夠大體用 "同時在線用戶數、用戶每秒操做次數、系統平均操做時間" 來計算.
keepAliveTimeout="6000" # 下次請求到來以前, Tomcat保持該鏈接6000ms.
maxKeepAliveRequests="10" # 該鏈接最大支持的請求數, 超過該請求數的鏈接也將被關閉(此時就會返回一個Connection: close頭給客戶端). 1表示禁用長鏈接, -1表示不限制鏈接個數, 默認爲100, 通常設置在100~200之間.
acceptorThreadCount="1" # 用於接收鏈接的線程的數量, 默認值是1. 通常若是服務器是多核CPU時, 須要改配置爲 2.
enableLookups="false" # 是否支持反查域名(即DNS解析), 默認爲true. 爲提升處理能力, 應設置爲false.
disableUploadTimeout="true" # 上傳時是否啓用超時機制, 若爲true, 則禁用上傳超時.
connectionTimeout="20000" # 網絡鏈接超時時間, 默認值爲20000ms, 設置爲0表示永不超時 —— 存在隱患. 一般可設置爲30000ms.
URIEncoding="UTF-8" # 指定Tomcat容器的URL編碼格式.
maxHttpHeaderSize="8192" # HTTP請求頭信息的最大程度, 超過此長度的部分不予處理. 通常設置爲8K便可.
maxPostSize="10485760" # 指定POST請求的內容大小, 單位爲Byte, 默認大小爲2097152(2MB), 10485760爲10M. 若是要禁用限制, 可設置爲-1.
compression="on" # 打開傳輸時壓縮功能.
compressionMinSize="10240" # 啓用壓縮的輸出內容大小, 默認爲2048, 即2KB.
noCompressionUserAgents="gozilla, traviata" # 設置不啓用壓縮的瀏覽器
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" # 壓縮的資源類型

2) 補充說明:

1>. Tomcat 的壓縮是在客戶端請求服務器對應資源後, 從服務器端將資源文件壓縮, 再輸出到客戶端, 由客戶端的瀏覽器負責解壓縮並瀏覽. 相對於普通的瀏覽過程(如瀏覽HTML、CSS、Javascript和Text), 它能夠節省40%左右的流量. 更爲重要的是, 它也能夠對動態生成的網頁(包括CGI、PHP、JSP、ASP、Servlet、SHTML等)進行壓縮. 2>. 須要注意的是, 壓縮會增長Tomcat的負擔, 最好採用 Nginx + Tomcat 或 Apache + Tomcat 方式, 將壓縮交由 Nginx / Apache 去完成. 在server.xml的節點配置(還沒有驗證使用):

  • <Service name="Catalina" /> --- 處理全部直接由Tomcat服務器接收的web客戶請求.
  • <Service name="Apache" /> --- 處理全部由Apahce服務器轉發過來的Web客戶請求.
  • <Service name="Nginx" /> --- 處理全部由Nginx服務器轉發過來的Web客戶端請求.

2.1.3 使用NIO模式處理請求

1) 默認配置 - BlockingIO模型:

<Connector port="8080" protocol="HTTP/1.1" 
           connectionTimeout="20000" 
           redirectPort="8443" />

2) 關於NIO的說明:

  • 每一個Web客戶端請求對服務器端來講就是一個單獨的線程, 客戶端請求數量增多, 服務器端的處理線程數量也將增長, 對CPU而言, 將會在線程切換上消耗更多的時間. 而NIO則是使用單線程(單個CPU)或只使用少許的多線程(多CPU)來接受Socket, 而由線程池來處理堵塞在 Pipe 或 Queue 中的請求. 這樣的話, 只要OS能夠接受TCP鏈接, Web服務器就能夠處理該請求 -- 大大提升了Web服務器的伸縮性.
  • Tomcat 8 下使用 NIO2, 即 org.apache.coyote.http11.Http11Nio2Protocol 更優.
  • Tomcat 六、7 下使用 NIO, 即 org.apache.coyote.http11.Http11NioProtocol 更優.

3) NIO模型配置:

<Connector executor="tomcatThreadPool" 
           port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
           connectionTimeout="20000" 
           redirectPort="8443" 
           maxPostSize="10485760" 
           acceptorThreadCount="2" />

4) 參數說明:

  • executor="..." # 鏈接器使用的線程池名稱.
  • port="..." # 鏈接端口, URL中指定此端口進行訪問.
  • protocol="..." # 鏈接器使用的請求處理模式.
  • redirectPort="8443" # 基於SSL的端口, 在須要基於安全通道的場合, 好比當客戶端的請求協議是HTTPS時, 將該請求轉發到此8443端口.

2.1.4 使用APR模式處理請求

能夠簡單地將APR模式理解爲,Tomcat將以JNI的形式調用Apache HTTP服務器的核心動態連接庫, 進行文件讀取或網絡傳輸操做, 從而大大地提升Tomcat對靜態文件的處理性能.
APR是Tomcat上運行高併發應用的首選模式, 同時若是使用HTTPS方式傳輸, 也能夠提高SSL的處理性能.
前面已經提到, APR模式會調用操做系統底層的IO接口, 因此須要安裝必要的依賴.

1) 安裝OpenSSL:

安裝命令以下:
yum -y install openssl-devel

2) 安裝APR組件:

  • yum安裝:
    • yum -y install apr-devel apr apr-util tomcat-native
  • 源碼安裝
    • 須要下載的包:apr-<version>.tar.gz和apr-util-<version>.tar.gz
    • 下載地址: http://mirrors.aliyun.com/apache/apr/
    • 安裝: 解壓, 編譯, 安裝,在此囉嗦,不會的本身百度下.
  • 配置環境變量: 上面源碼安裝完後設置下環境變量:
    • export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib
    • 或者, 將/usr/local/apr/lib包路徑添加到/etc/ld.so.conf文件中:
    • echo "/usr/local/apr/lib" >> /etc/ld.so.conf

源碼安裝常見錯誤及解決:

可能出現gcc依賴沒有安裝的錯誤, 可經過 yum install gcc 命令安裝.
若是make過程當中出錯, 解決錯誤後從新安裝前須要執行清理: make clean, 而後再次嘗試make及make install過程.
若是拋出 xml/apr_xml.c:35:19: error: expat.h: No such file or directory, 說明缺乏了expat庫, 可執行下屬命令安裝: yum install expat-devel.

3) 安裝tomcat-native組件:

tomcat-native組件能夠看做是Tomcat與APR交互的中間環節.

tomcat-native?是什麼?前面沒有叫下載啊?
確實,我剛開始在網上搜索的時候也是很困惑的,但是有一我的說了,「就在下載的tomcat的bin目錄下面」,我去看了一下,果真有!!
將咱們安裝好的tomcat的bin目錄下的 tomcat-native.tar.gz 文件複製到 /usr/local/src 中,而且解壓縮,獲得目錄tomcat-native-<version>-src 在這個目錄中有相關的說明,告訴咱們如何構建。

進入到目錄中的 jni/native 目錄內,這個目錄內的文件就是咱們須要的文件,依次執行下面的命令

./configure --with-apr=/usr/local/apr --with-java-home=/usr/java/jdk --with-ssl=yes
make
make install

在這裏,apr的目錄要使用前面安裝apr的時候的目錄,若是修改了的話,還請對應修改,java的目錄要使用jdk的根目錄,若是不是這個也請修改。

執行上面的命令以後,會在目錄

/usr/local/apr/lib
中生成對應的文件,能夠查看文件,確認安裝成功。也能夠根據每一步執行命令的輸出來判斷成功沒有,如有問題的話,要及時解決,在進行後續操做。

4) Tomcat整合APR:

  • 第一步: 修改啓動腳本catalina.sh:
    ${TOMCAT_HOME}/bin/catalina.sh 文件的 cygwin=false 前(110行左右)加入下述啓動參數:

    JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/usr/local/apr/lib"
  • 第二步: 修改容器配置文件server.xml:

    查看 ${TOMCAT_HOME}/conf/server.xml 文件, 確保以下監聽器沒有被註釋掉:

    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  • 修改Connector選項:

    <Connector port="8443" 
                protocol="org.apache.coyote.http11.Http11AprProtocol" 
                maxThreads="150" SSLEnabled="true" >
    </Connector>

5) 驗證配置是否成功:

啓動Tomcat, 在 ${TOMCAT_HOME}/logs/catalina.out 文件中查看日誌信息:

  • 若是出現下述內容, 說明APR組件安裝不成功:

    Sep 14, 2018 19:11:20 PM org.apache.catalina.core.AprLifecycleListener init 
    INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path:...
  • 若是出現下述內容, 說明APR組件安裝成功:

    Sep 14, 2018 19:19:47 PM org.apache.catalina.core.AprLifecycleListener init
    INFO: Loaded APR based Apache Tomcat Native library 1.1.27 using APR version 1.6.3.
    Sep 14, 2018 19:19:47 PM org.apache.catalina.core.AprLifecycleListener init
    INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
    Sep 14, 2018 19:19:47 PM org.apache.catalina.core.AprLifecycleListener initializeSSL
    INFO: OpenSSL successfully initialized (OpenSSL 1.0.1e-fips 11 Feb 2013)
    Sep 14, 2018 19:19:47 PM org.apache.coyote.AbstractProtocol init
    INFO: Initializing ProtocolHandler ["http-apr-8080"]
    Sep 14, 2018 19:19:47 PM org.apache.coyote.AbstractProtocol init
    INFO: Initializing ProtocolHandler ["ajp-apr-8009"]
  • Tomcat經過APR模式成功啓動:

    Sep 14, 2018 19:19:56 PM org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["http-apr-8986"]
    Sep 14, 2018 19:19:56 PM org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["ajp-apr-8915"]
    Sep 14, 2018 19:19:56 PM org.apache.catalina.startup.Catalina start
    INFO: Server startup in 9421 ms

2.2 配置AJP鏈接器

AJP(Apache JServer Protocol)是爲 Tomcat 與 HTTP 服務器之間通訊而定製的協議, 能提供較高的通訊速度和效率.
AJP v13 協議是面向包的, Web服務器和Servlet容器經過TCP鏈接來交互, 爲了節省 建立Socket的昂貴代價, Web服務器會嘗試維護一個永久的TCP鏈接到Servlet容器, 並在多個請求與響應週期過程內重用該TCP鏈接.

若是使用Apache架構, 就要用AJP鏈接器, 當Apache接收到動態網頁請求時, 經過在配置中指定的端口號將請求發送給在此端口號上監聽的AJP鏈接器組件.
若是不使用Tomcat + Apache, 而是用其餘架構, 如Tomcat + Nginx, 就須要註銷掉該鏈接器.
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->

3. setenv.sh相關配置

3.1 基於JDK7配置建議

在Linux環境下設置Tomcat JVM,在/opt/tomcat/bin/catalina.sh文件中找到"

# ----- Execute The Requested Command"位置,設置JVM以下:
# ----- Execute The Requested Command -----------------------------------------
JAVA_OPTS="$JAVA_OPTS -server -Xms3072m -Xmx3072m -XX:PermSize=1024M -XX:MaxPermSize=1024M"

參數說明:
-Xms:設置JVM最小內存。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。
-Xmx:設置JVM最大可用內存。
-XX:NewSize:設置年輕代大小
-XX:PermSize:設置永久代大小
-XX:MaxPermSize:設置最大永久代大小

JVM內存模型
一、Java棧
Java棧是與每個線程關聯的,JVM在建立每個線程的時候,會分配必定的棧空間給線程。
它主要用來存儲線程執行過程當中的局部變量,方法的返回值,以及方法調用上下文。棧空間隨着線程的終止而釋放。
StackOverflowError:若是在線程執行的過程當中,棧空間不夠用,那麼JVM就會拋出此異常,這種狀況通常是死遞歸形成的。

二、堆
Java中堆是由全部的線程共享的一塊內存區域,堆用來保存各類JAVA對象,好比數組,線程對象等。

三、Java 的內存模型
a、Young,年輕代(易被 GC)
Young 區被劃分爲三部分,Eden 區和兩個大小嚴格相同的 Survivor 區
其中 Survivor 區間中,某一時刻只有其中一個是被使用的,另一個留作垃圾收集時複製對象用,在 Young 區間變滿的時候,minor GC 就會將存活的對象移到空閒的Survivor 區間中,根據 JVM 的策略,在通過幾回垃圾收集後,任然存活於 Survivor 的對象將被移動到 Tenured 區間。

b、Tenured,終身代
Tenured 區主要保存生命週期長的對象,通常是一些老的對象,當一些對象在 Young 複製轉移必定的次數之後,對象就會被轉移到 Tenured 區,通常若是系統中用了 application 級別的緩存,緩存中的對象每每會被轉移到這一區間。

c、Perm,永久代
主要保存 class,method,filed 對象,這部門的空間通常不會溢出,除非一次性加載了不少的類,不過在涉及到熱部署的應用服務器的時候,有時候會遇到 java.lang.OutOfMemoryError : PermGen space 的錯誤,形成這個錯誤的很大緣由就有多是每次都從新部署,可是從新部署後,類的 class 沒有被卸載掉,這樣就形成了大量的 class 對象保存在了 perm 中,這種狀況下,通常從新啓動應用服務器能夠解決問題。

若是服務器只運行一個 Tomcat:
機子內存若是是 8G,通常 PermSize 配置是主要保證系統能穩定起來就行:

JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms6144m -Xmx6144m -XX:NewSize=1024m -XX:MaxNewSize=2048m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"

機子內存若是是 16G,通常 PermSize 配置是主要保證系統能穩定起來就行:

JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms13312m -Xmx13312m -XX:NewSize=3072m -XX:MaxNewSize=4096m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"

機子內存若是是 32G,通常 PermSize 配置是主要保證系統能穩定起來就行:

JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms29696m -Xmx29696m -XX:NewSize=6144m -XX:MaxNewSize=9216m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"

若是是開發機:
-Xms550m -Xmx1250m -XX:PermSize=550m -XX:MaxPermSize=1250m

參數說明:
-Dfile.encoding:默認文件編碼
-server:表示這是應用於服務器的配置,JVM 內部會有特殊處理的
-Xmx1024m:設置JVM最大可用內存爲1024MB
-Xms1024m:設置JVM最小內存爲1024m。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。
-XX:NewSize:設置年輕代大小
-XX:MaxNewSize:設置最大的年輕代大小
-XX:PermSize:設置永久代大小
-XX:MaxPermSize:設置最大永久代大小
-XX:NewRatio=4:設置年輕代(包括 Eden 和兩個 Survivor 區)與終身代的比值(除去永久代)。設置爲 4,則年輕代與終身代所佔比值爲 1:4,年輕代佔整個堆棧的 1/5
-XX:MaxTenuringThreshold=10:設置垃圾最大年齡,默認爲:15。若是設置爲 0 的話,則年輕代對象不通過 Survivor 區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在 Survivor 區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的概論。
-XX:+DisableExplicitGC:這個將會忽略手動調用 GC 的代碼使得 System.gc() 的調用就會變成一個空調用,徹底不會觸發任何 GC

3.2 基於JDK8配置建議

Tomcat容器是運行在JVM上的, 其默認內存通常都很小(物理內存的1/64), 在實際生產環境中, 若不配置則會極大浪費服務器資源, 影像系統的性能. 能夠經過調整JVM啓動參數, 使得Tomcat擁有更好的性能.

對於JVM的優化主要有兩個方面: JVM內存調優 和 垃圾收集策略調優

3.2.1 JVM內存調優

Tomcat運行內存 = Xmx(初始內存大小) + Perm Generation(JDK 7中的永久代大小) + Java應用建立的線程數 * 1MB.

Java 應用每建立一個線程, JVM 進程的內存中就會建立一個 Thread 對象, 同時也會在操做系統中建立一個真正的物理線程(參考JVM規範), 操做系統會在 Tomcat 的空閒內存中建立這個物理線程, 而不是在 JVM 的 Xmx 堆內存中建立.

在 JDK 1.4中, 默認的棧大小是256KB/線程, 但自 JDK 5(爲了推廣的方便, JDK 後續版本再也不是1.x命名)開始, 默認的棧大小變爲1M/線程. 舉例: 若是系統剩餘內存爲400M, 則Java應用最多能建立400個可用線程.

因此: 要想建立更多的線程, 必須減小分配給JVM的最大內存.

內存配置相關參數:

  • -server
    • JVM的server模式, 在多CPU服務器中性能能夠獲得更好地發揮. 默認爲client. 配置server模式時要將其做爲第一個參數.
  • -Xmx4g
    • 最大堆內存, 默認爲物理內存的1/4(已在JDK 7下驗證, 最大值爲30638MB, 總內存126GB的23.75%).
    • 在只運行Tomcar容器的服務器中, 建議設置爲物理內存的50%~80%.
  • -Xms4g
    • 初始堆內存大小, 默認值爲物理內存的1/64(已在JDK 7下驗證, 內存126GB, 初始值爲2GB). 
  • -Xss128k
    • 每一個線程的Stack大小. 在相同物理內存下, 減少這個值能生成更多的線程, 可是操做系統對一個進程內的線程數是有限制的, 經驗範圍是3000~5000.
  • -XX:NewRatio=4
    • 設置新生代(包括Eden和兩個Survivor區)與老年代的比值(除去持久代), 默認爲2, 即新生代與老年代所佔比值爲1:2, 新生代佔整個堆棧的1/3.
  • -XX:SurvivorRatio=4
    • 設置新生代中Eden區與1個Survivor區的大小比值. 默認爲8, 即Eden區佔新生代的80%, 2個Survivor分別佔新生代的10%.
    • 設置爲4, 則兩個Survivor區與一個Eden區的比值爲2:4, 一個Survivor區佔整個新生代的1/6.
  • -Xmn1024m
    • 設置Young Generation所佔用的Java Heap大小爲1g. 此值對系統性能影響較大, Sun官方推薦配置爲整個堆的3/8(或Xmx的1/4~1/3左右). 
    • 也可以使用-XX:NewSize和-XX:MaxNewsize設置新生代的初始值和最大值.
    • 注意: -Xmn 與 -XX:NewSize、-XX:MaxNewSize 的優先級: -XX:NewRatio的值會被忽略.
    • 1. 高優先級: -XX:NewSize/-XX:MaxNewSize
    • 2. 中優先級: -Xmn, 等效於同時設置 -Xmn = -XX:NewSize = -XX:MaxNewSize 三者的值
    • 3. 低優先級: -XX:NewRatio
    • -Xmn參數是在JDK 1.4 開始支持, 推薦使用之.
  • -XX:NewSize=1g
    • 設置新生代的大小, 默認爲1.25MB(已在JDK 7下驗證). 若顯示設置此值, 將使得NewRatio選項失效.
  • -XX:OldSize=2g
    • 設置老年代的大小, 默認爲5.1875MB(已在JDK 7下驗證).
  • -XX:PermSize=128m
    • JDK 7及如下版本適用: 設置Java Heap中永久代的初始大小, 默認爲20.75MB(已在JDK 7下驗證, client、server模式下均相同).
  • -XX:MaxPermSize=256m
    • JDK 7及如下版本適用: 設置Java Heap中永久代的最大值. 默認爲82.0MB(已在JDK 7下驗證, client、server模式下均相同).
  • -XX:MetaspaceSize=128m
    • JDK 8及以上版本適用: 初始元空間的大小, 默認爲21MB(實際爲20.79MB左右, 已驗證).
  • -XX:MaxMetaspaceSize=256m
    • JDK 8及以上版本適用: 最大元空間的大小. 默認無上限(在126GB物理內存的服務器中, 默認值爲(2^44-1)MB, 已驗證).

注意:

① 若是不指定Xmx、Xms和NewSize、OldSize, 則系統將基於Xms=1/64總內存大小, 對各個Space按照NewRatio=2進行空間的分配, 此時NewSize與OldSize的默認大小將失效.

② 若是指定了Xmx、Xms, 未指定NewSize、OldSize, 則系統將優先知足 NewRatio=2, 且OldSize+NewSize=Xms, 此時NewSize與OldSize的默認大小將失效.

結論: 除非顯式指定NewSize與OldSize的值, 不然它們的默認配置通常都不會獲得知足. 顯式指定其中任一個, 另外一個就會基於默認值, 並根據應用程序的消耗動態分配空間大小.

3.2.2 tomcat JVM內存調優

 JVM內存方面的調優, 須要在${TOMCAT_HOME}/bin/catalina.sh文件中調整, 配置 JAVA_OPTS 變量便可. 在啓動Tomcat時, 會執行catalina.sh中的腳本, 將 JAVA_OPTS 做爲JVM的啓動參數進行處理.

 具體可參考以下配置(示例服務器配置: 126g的物理內存, 2個10核心20線程的物理CPU):

在文件最前面(即cygwin=false以前)設置, $JAVA_OPTS 的做用是保留原有的設置, 防止這次修改覆蓋以前的設置

JAVA_OPTS="$JAVA_OPTS -Xmx96g -Xms96g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

1) 說明:
① 若不指定Xmx, 即Java Heap的最大值, 程序啓動時, JVM會調整其大小以知足程序運行的須要. 每次調整時, 都會對堆進行一次徹底垃圾收集(即Full GC), 比較影響性能. 所以推薦明確指定Xmx的大小.
② 令Xmx=Xms會有更好地性能表現: 能避免JVM在每次垃圾收集後從新動態調節堆空間, 由於頻繁伸縮堆大小將帶來額外的性能消耗.
③ JVM有2種模式: client客戶端模式 和 server服務端模式 , 平時開發中使用的可能是默認的client模式. 可經過命令行參數-server強制開啓server模式. 二者之間的最大區別是, JVM對server模式作了大量優化: 雖然server模式下應用程序啓動較慢, 但在長時間運行下, 程序運行效率會明顯高於client模式, 即 client模式不適合須要長時間運行的項目 .

2) 元空間的調優:
元空間的大小將受限於機器的內存的限制. 限制類的元數據的內存大小, 以免出現虛擬內存切換以及本地內存分配失敗.
若是可能出現類加載器泄漏, 應當配置此參數指定大小. 32位機器上, 若是地址空間可能會被耗盡, 也應當配置此參數.
元空間的初始大小是21M——這是GC的初始高水位線, 超過這個大小會進行Full GC來進行類的收集.
若是啓動後GC過於頻繁, 請將該值設置得大一些, 以便推遲GC的執行時間.

3.2.3 JVM垃圾回收(GC)策略調優

 Tomcat的GC策略通常都是與其內存參數一塊兒配置的, 與應用複雜度相匹配的GC策略、與服務器性能相適應的內存比例, 都將使得系統性能獲得大幅提高.

GC策略方面的調優, 也是在 ${TOMCAT_HOME}/bin/catalina.sh文件中調整 -- 一樣是配置 JAVA_OPTS 變量便可.

JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100"

參數說明:

  • -XX:+UseSerialGC: 設置串行收集器(JDK1.5之前主要的回收方式)
  • -XX:+UseParallelGC:選擇垃圾收集器爲並行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用併發收集,而年老代仍舊使用串行收集。
  • -XX:ParallelGCThreads=20:配置並行收集器的線程數,即:同時多少個線程一塊兒進行垃圾回收。此值最好配置與處理器數目相等。 
  • -XX:+UseParallelOldGC:配置年老代垃圾收集方式爲並行收集。JDK6.0支持對年老代並行收集 
  • -XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間,若是沒法知足此時間,JVM會自動調全年輕代大小,以知足此值。
  • -XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開。
# 並行收集器(吞吐量優先)
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 
# 併發收集器(響應時間優先)
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC

參數說明:

  • -XX:+UseConcMarkSweepGC:設置年老代爲併發收集。測試中配置這個之後,-XX:NewRatio=4的配置失效了,緣由不明。因此,此時年輕代大小最好用-Xmn設置。 
  • -XX:+UseParNewGC: 設置年輕代爲並行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,因此無需再設置此值。 
  • -XX:CMSFullGCsBeforeCompaction:因爲併發收集器不對內存空間進行壓縮、整理,因此運行一段時間之後會產生「碎片」,使得運行效率下降。此值設置運行多少次GC之後對內存空間進行壓縮、整理。 
  • -XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,可是能夠消除碎片 

以JDK 八、Tomcat 8爲例, 服務器內存爲128GB, 做出以下配置:

經過Solr集羣大批量導入數據的應用中, Parallel GC策略的暫停時間太長, 因此選擇CMS收集器.

# 下述配置各自獨佔一行, 要置於"cygwin=false"以前. 
# 配置內存
JAVA_OPTS="-server -Xmx96g -Xms96g -Xmn35g -XX:OldSize=55g -Xss128k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"

# 配置GC策略
JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15 -XX:CMSInitiatingOccupancyFraction=40 -XX:CMSFullGCsBeforeCompaction=0 -XX:+ExplicitGCInvokesConcurrent -XX:SoftRefLRUPolicyMSPerMB=0 -XX:MaxGCPauseMillis=100 -Xnoclassgc"

其中Java Heap的初始大小和最大大小均設置爲96g, 76%的物理內存(以不超過80%爲宜).

小結:

在內存設置中須要作一下權衡 1)內存越大,通常狀況下處理的效率也越高,但同時在作垃圾回收的時候所須要的時間也就越長,在這段時間內的處理效率是必然要受影響的。 2)在大多數的網絡文章中都推薦 Xmx和Xms設置爲一致,說是避免頻繁的回收,這個在測試的時候沒有看到明顯的效果,內存的佔用狀況基本都是鋸齒狀的效果,因此這個還要根據實際狀況來定。

相關文章
相關標籤/搜索