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设置为一致,说是避免频繁的回收,这个在测试的时候没有看到明显的效果,内存的占用状况基本都是锯齿状的效果,因此这个还要根据实际状况来定。

相关文章
相关标签/搜索