总结tomcat优化方法

1 Tomcat 性能优化

在目前流行的互联网架构中,Tomcat在目前的网络编程中是举足轻重的,由于Tomcat的运行依赖于JVM,从虚拟机的角度把Tomcat的优化分为外部环境调优 JVM 和 Tomcat 自身调优两部分

1.1 JVM组成部分

  • 类加载子系统:使用Java语言编写。java Source Code文件,通过javac编译成。class Byte Code文件。class loader类加载器将所需所有类加载到内存,必要时将类实例化成实例
  • 运行时数据区:最消耗内存的空间,需要优化
  • 执行引擎:包括JIT (JustInTimeCompiler)即时编译器,GC垃圾回收器
  • 本地方法接口:将本地方法栈通过JNI(Java Native Interface)调用Native Method Libraries,比如:C,C++库等,扩展Java功能,融合不同的编程语言为Java所用

JVM运行时数据区域由下面部分构成:

  • Method Area (线程共享):方法区是所有线程共享的内存空间,存放已加载的类信息(构造方法,接口定义),常量(final),静态变量(static),运行时常量池等。但实例变量存放在堆内存中。从JDK8开始此空间由永久代改名为元空间
  • heap (线程共享):堆在虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出OOM异常。堆是靠GC垃圾回收器管理的,通过-Xmx -Xms 指定最大堆和最小堆空间大小
  • Java stack (线程私有):Java栈是每个线程会分配一个栈,存放java中8大基本数据类型,对象引用,实例的本地变量,方法参数和返回值等,基于FILO()(First In Last Out),每个方法为一个栈帧
  • Program Counter Register (线程私有):PC寄存器就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令。因为线程需要切换,当一个线程被切换回来需要执行的时候,知道执行到哪里了
  • Native Method stack (线程私有):本地方法栈为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。

所谓本地方法,使用native 关健字修饰的方法,比如:Thread.sleep方法。简单的说是非Java实现的方法,例如操作系统的C编写的库提供的本地方法,Java调用这些本地方法接口执行。但是要注意,本地方法应该避免直接编程使用,因为Java可能跨平台使用,如果用了Windows API,换到了Linux平台部署就有了问题

1.2 GC (Garbage Collection) 垃圾收集器

在堆内存中如果创建的对象不再使用,仍占用着内存,此时即为垃圾。需要及时进行垃圾回收,从而释放内存空间给其它对象使用

其实不同的开发语言都有垃圾回收问题,C,C++需要程序员人为回收,造成开发难度大,容易出错等问题,但执行效率高,而JAVA和Python中不需要程序员进行人为的回收垃圾,而由JVM或相关程序自动回收垃圾,减轻程序员的开发难度,但可能会造成执行效率低下

堆内存里面经常创建、销毁对象,内存也是被使用、被释放。如果不妥善处理,一个使用频繁的进程,可能会出现虽然有足够的内存容量,但是无法分配出可用内存空间,因为没有连续成片的内存了,内存全是碎片化的空间。

所以需要有合适的垃圾回收机制,确保正常释放不再使用的内存空间,还需要保证内存空间尽可能的保持一定的连续

对于垃圾回收,需要解决三个问题:

  • 哪些是垃圾要回收
  • 怎么回收垃圾
  • 什么时候回收垃圾
1.2.1 Garbage 垃圾确定方法
  • 引用计数:每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该对象所占用堆内存就可以被回收。循环引用的对象都无法将引用计数归零,就无法清除。Python中即使用此种方式
  • 根搜索(可达)算法 Root Searching
1.2.2垃圾回收基本算法
1.2.2.1 标记-清除 Mark-Sweep

分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,对未标记对象(即不再使用的对象)逐一进行清理。

标记-清除最大的问题是会造成内存碎片,但是不浪费空间,效率较高(如果对象较多,逐一删除效率也会影响)

1.2.2.2 标记-压缩 (压实)Mark-Compact

分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。

标记-压缩算法的好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。
缺点是内存整理过程有消耗,效率相对低下

1.2.2.3 复制 Copying

先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。

缺点是比较浪费内存,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。
好处是没有碎片,复制过程中保证对象使用连续空间,且一次性清除所有垃圾,所以效率很高

1.2.2.4 多种算法总结

没有最好的算法,在不同场景选择最合适的算法

  • 效率:标记清除算法>复制算法> 标记压缩算法
  • 内存整齐度:复制算法=标记压缩算法> 标记清除算法
  • 内存利用率:标记压缩算法=标记清除算法>复制算法
1.2.2.5 STW

对于大多数垃圾回收算法而言,GC线程工作时,停止所有工作的线程,称为Stop The World。GC 完成时,恢复其他工作线程运行。这也是JVM运行中最头疼的问题。

1.2.3 分代堆内存GC策略

对不同数据进行区分管理,不同分区对数据实施不同回收策略,分而治之

1.2.3.1 堆内存分代

将heap内存空间分为三个不同类别:年轻代、老年代、持久代

Heap堆内存分为

  • 年轻代Young:Young Generation

  • 伊甸园区eden:只有一个,刚刚创建的对象

  • 幸存(存活)区Servivor Space:有2个幸存区,一个是from区,一个是to区。大小相等、地位相同、可互换。

    • from 指的是本次复制数据的源区
    • to 指的是本次复制数据的目标区
  • 老年代Tenured:Old Generation,长时间存活的对象

永久代:JDK1.7之前使用,即Method Area方法区,保存JVM自身的类和方法,存储JAVA运行时的环境信息,JDK1.8后改名为 MetaSpace,此空间不存在垃圾回收,关闭JVM会释放此区域内存,此空间物理上不属于heap内存,但逻辑上存在于heap内存

  • 永久代必须指定大小限制,字符串常量JDK1.7存放在永久代,1.8后存放在heap中

  • MetaSpace 可以设置,也可不设置,无上限

规律:一般情况99%的对象都是临时对象

查看JVM内存分配情况

[root@centos7 ~]#cat Heap.java
public class Heap {
    public static void main(String[] args){
        //返回虚拟机试图使用的最大内存,字节单位
        long max = Runtime.getRuntime().maxMemory();
        //返回JVM初始化总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max="+max+"字节\t"+(max/(double)1024/1024)+"MB");
        System.out.println("total="+total+"字节\t"+(total/(double)1024/1024)+"MB");
    }
}

[root@centos7 ~]#javac Heap.java
[root@centos7 ~]#java -classpath . Heap
max=462422016字节	441.0MB
total=32505856字节	31.0MB
[root@centos7 ~]#free -m
              total        used        free      shared  buff/cache   available
Mem:           1980         275        1495           9         209        1552
Swap:          2047           0        2047
[root@centos7 ~]#echo 441*4|bc
1764
[root@centos7 ~]#echo 1980/31|bc
63
#默认JVM试图分配的最大内存为总内存的1/4,初始化默认总内存为总内存的1/64

[root@centos7 ~]#java -XX:+PrintGCDetails -cp . Heap
max=462422016字节	441.0MB
total=32505856字节	31.0MB
Heap
 PSYoungGen      total 9728K, used 870K [0x00000000f5b00000, 0x00000000f6580000, 0x0000000100000000)
  eden space 8704K, 10% used [0x00000000f5b00000,0x00000000f5bd9b88,0x00000000f6380000)
  from space 1024K, 0% used [0x00000000f6480000,0x00000000f6480000,0x00000000f6580000)
  to   space 1024K, 0% used [0x00000000f6380000,0x00000000f6380000,0x00000000f6480000)
 ParOldGen       total 22016K, used 0K [0x00000000e1000000, 0x00000000e2580000, 0x00000000f5b00000)
  object space 22016K, 0% used [0x00000000e1000000,0x00000000e1000000,0x00000000e2580000)
 Metaspace       used 2534K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 269K, capacity 386K, committed 512K, reserved 1048576K
[root@centos7 ~]#echo 9728+22016|bc
31744
[root@centos7 ~]#echo 31*1024|bc
31744
#说明年轻代+老年代占用了所有heap空间,Metaspace实际不占heap空间,逻辑上存在于Heap
1.2.3.2 年轻代回收 Minor GC
  1. 起始时,所有新建对象(特大对象直接进入老年代)都出生在eden,当eden满了,启动GC。这个称为Young GC 或者 Minor GC

  2. 标记eden存活对象,然后将存活对象复制到s0(假设本次是s0,也可以是s1,它们可以调换),eden剩余所有空间都清空。GC完成

  3. 继续新建对象,当eden再次满了,启动GC

  4. 先同时标记eden和s0中存活对象,然后将存活对象复制到s1。将eden和s0清空,此次GC完成

  5. 继续新建对象,当eden满了,启动GC

  6. 标记eden和s1中存活对象,然后将存活对象复制到s0。将eden和s1清空,此次GC完成

以后就重复上面的步骤。

通常场景下,大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。

但是,如果一个对象一直存活,它最后就在from、to来回复制,如果from区中对象复制次数达到阈值 (默认15次,CMS为6次,可通过java的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到老年代。

1.2.3.3 老年代回收 Major GC

进入老年代的数据较少,所以老年代区被占满的速度较慢,所以垃圾回收也不频繁。

如果老年代也满了,会触发老年代GC,称为Old GC或者 Major GC

由于老年代对象一般来说存活次数较长,所以较常采用标记-压缩算法。

当老年代满时,会触发 Full GC,即对所有"代"的内存进行垃圾回收

Minor GC比较频繁,Major GC较少。但一般Major GC时,由于老年代对象也可以引用新生代对象,所以先进行一次Minor GC,然后在Major GC会提高效率。可以认为回收老年代的时候完成了一次Full GC。

所以可以认为 MajorGC = FullGC

1.2.3.4 GC 触发条件

Minor GC 触发条件:当eden区满了触发
Full GC 触发条件:

  • 老年代满了
  • System.gc()手动调用。不推荐

年轻代:

  • 存活时长低
  • 适合复制算法

老年代:

  • 区域大,存活时长高
  • 适合标记压缩算法
1.2.4 java 内存调整相关参数
1.2.4.1 JVM 内存常用相关参数

选项分类

  • -选项名称 此为标准选项,所有HotSpot都支持
  • -X选项名称 此为稳定的非标准选项
  • -XX:选项名称 非标准的不稳定选项,下一个版本可能会取消
参数说明举例
-Xms设置应用程序初始使用的堆内存大小(年轻代+老年代)-Xms2g
-Xmx设置应用程序能获得的最大堆内存,早期JVM不建议超过32G,内存管理效率下降-Xms4g
-XX:NewSize设置初始新生代大小-XX:NewSize=128m
-XX:MaxNewSize设置最大新生代内存空间-XX:MaxNewSize=256m
-Xmnsize同时设置-XX:NewSize 和 -XX:MaxNewSize,代替两者-Xmn1g
-XX:NewRatio以比例方式设置新生代和老年代-XX:NewRatio=2 new/old=1/2
-XX:SurvivorRatio以比例方式设置eden和survivor(S0或S1)-XX:SurvivorRatio=6 eden/survivor=6/1 new/survivor=8/1
-Xss设置每个线程私有的栈空间大小,依据具体线程大小和数量-Xss256k

查看java的选项帮助

#查看java命令标准选项
[root@centos7 ~]#java

#查看java的非标准选项
[root@centos7 ~]#java -X

#查看所有不稳定选项的当前生效值
[root@centos7 ~]#java -XX:+PrintFlagsFinal

#查看所有不稳定选项的默认值
[root@centos7 ~]#java -XX:+PrintFlagsInitial

指定内存空间

[root@centos7 ~]#java -Xms1024m -Xmx1024m -XX:+PrintGCDetails -cp . Heap
max=1029177344字节	981.5MB
total=1029177344字节	981.5MB
Heap
 PSYoungGen      total 305664K, used 15729K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
  eden space 262144K, 6% used [0x00000000eab00000,0x00000000eba5c420,0x00000000fab00000)
  from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
  to   space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
 ParOldGen       total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
  object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
 Metaspace       used 2535K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 269K, capacity 386K, committed 512K, reserved 1048576K

1.2.4.2 Tomcat的JVM参数设置

在bin/catalina.sh中增加一行

[root@centos7 ~]#vim /usr/local/tomcat/bin/catalina.sh
# OS specific support.  $var _must_ be set to either true or false.
#添加下面一行
JAVA_OPTS="-server -Xms512m -Xmx512m -XX:NewSize=100m -XX:MaxNewSize=200m"

cygwin=false
darwin=false

#-server:VM运行在server模式,为在服务器端最大化程序运行速度而优化

#重启Tomcat服务
[root@centos7 ~]#systemctl restart tomcat.service

#确认参数设置
[root@centos7 ~]#ps aux|grep tomcat
tomcat     1781 30.7  7.7 3040164 156628 ?      Sl   08:18   0:33 /usr/local/jdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms512m -Xmx512m -XX:NewSize=100m -XX:MaxNewSize=200m -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs=-classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomca -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
root       1816  0.0  0.0 112808   968 pts/0    S+   08:20   0:00 grep --color=auto tomcat

1.2.5 垃圾收集方式

工作模式不同:指的是GC线程和工作线程是否一起运行

  • 独占垃圾回收器:只有GC在工作,STW 一直进行到回收完毕,工作线程才能继续执行
  • 并发垃圾回收器:让GC线程垃圾回收某些阶段可以和工作线程一起进行,如:标记阶段并行,回收阶段仍然串行

回收线程数:指的是GC线程是否串行或并行执行

  • 串行垃圾回收器:一个GC线程完成回收工作
  • 并行垃圾回收器:多个GC线程同时一起完成回收工作,充分利用CPU资源
1.2.6 调整策略

对JVM调整策略应用极广

  • 在WEB领域中Tomcat等
  • 大数据领域Hadoop生态各组件
  • 在消息中间件领域的Kafka等
  • 在搜索引擎领域的ElasticSearch、Solr等

注意:在不同领域和场景对JVM需要不同的调整策略

  • 减少 STW 时长,串行变并行
  • 减少 GC 次数,要分配合适的内存大小

一般情况下,大概可以使用以下原则:

  • 客户端或较小程序,内存使用量不大,可以使用串行回收
  • 对于服务端大型计算,可以使用并行回收
  • 大型WEB应用,用户端不愿意等待,尽量少的STW,可以使用并发回收
1.2.7 垃圾回收器
1.2.7.1 按分代设置不同垃圾回收器

新生代

  • 新生代串行收集器Serial:单线程、独占式串行,采用复制算法,简单高效但会造成STW

  • 新生代并行回收收集器PS(Parallel Scavenge):多线程并行、独占式,会产生STW,使用复制算法,关注调整吞吐量,此收集器关注点是达到一个可控制的吞吐量

吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),比如虚拟机总共运行100分钟,其中垃圾回收花掉1分钟,那吞吐量就是99%。

高吞吐量可以高效率利用CPU时间,尽快完成运算任务,主要适合在后台运算而不需要太多交互的任务。

除此之外,Parallel Scavenge 收集器具有自适应调节策略,它可以将内存管理的调优任务交给虚拟机去完成。自适应调节策略也是Parallel Scavenge与 ParNew 收集器的一个重要区别。

和ParNew不同,PS不可以和老年代的CMS组合

  • 新生代并行收集器ParNew:就是Serial 收集器的多线程版,将单线程的串行收集器变成了多线程并行、独占式,使用复制算法,相当于PS的改进版

经常和CMS配合使用,关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,适合需要与用户交互的程序,良好的响应速度能提升用户体验

老年代

  • 老年代串行收集器Serial Old:Serial Old是Serial收集器的老年代版本,单线程、独占式串行,回收算法使用标记压缩
  • 老年代并行回收收集器Parallel Old:多线程、独占式并行,回收算法使用标记压缩,关注调整吞吐量

Parallel Old收集器是Parallel Scavenge 收集器的老年代版本,这个收集器是JDK1.6之后才开始提供,Parallel Scavenge 收集器无法与CMS收集器配合工作,因为一个是为了吞吐量,一个是为了客户体验(也就是暂停时间的缩短)

  • CMS (Concurrent Mark Sweep并发标记清除算法) 收集器
    在某些阶段尽量使用和工作线程一起运行,减少STW时长(200ms以内),提升响应速度,是互联网服务端BS系统上较佳的回收算法

分为4个阶段:初始标记、并发标记、重新标记、并发清除,在初始标记、重新标记时需要STW。

初始标记:此过程需要STW(Stop The Word),只标记一下GC Roots能直接关联到的对象,速度很快。

并发标记:就是GC Roots进行扫描可达链的过程,为了找出哪些对象需要收集。这个过程远远慢于初始标记,但它是和用户线程一起运行的,不会出现STW,所有用户并不会感受到。

重新标记:为了修正在并发标记期间,用户线程产生的垃圾,这个过程会比初始标记时间稍微长一点,但是也很快,和初始标记一样会产生STW。

并发清理:在重新标记之后,对现有的垃圾进行清理,和并发标记一样也是和用户线程一起运行的,耗时较长(和初始标记比的话),不会出现STW。

由于整个过程中,耗时最长的并发标记和并发清理都是与用户线程一起执行的,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

JVM 1.8 默认的垃圾回收器:PS + Parallel Old,所以大多数都是针对此进行调优

1.2.7.2 垃圾收集器设置

优化调整Java 相关参数的目标:尽量减少FullGC和STW

通过以下选项可以单独指定新生代、老年代的垃圾收集器

  • -server 指定为Server模式,也是默认值,一般使用此工作模式
  • -XX:+UseSerialGC
    • 运行在Client模式下,新生代是Serial,老年代使用Serial Old
  • -XX:+UseParNewGC
    • 新生代使用ParNew,老年代使用Serial Old
  • -XX:+UseParallelGC
    • 运行于server模式下,新生代使用Parallel Scavenge , 老年代使用Serial Old
  • -XX:+UseParallelOldGC
    • 新生代使用Paralell Scavenge, 老年代使用Paralell Old
  • -XX:ParallelGCThreads=N,在关注吞吐量的场景使用此选项增加并行线程数
  • -XX:+UseConcMarkSweepGC
    • 新生代使用ParNew,老年代优先使用CMS,备选方式为Serial Old
    • 响应时间要短,停顿短使用这个垃圾收集器
  • -XX:CMSInitiatingOccupancyFraction=N,N为0-100整数表示达到老年代的大小的百分比多少触发回收,默认68
    • -XX:+UseCMSCompactAtFullCollection 开启此值,在CMS收集后,进行内存碎片整理
  • -XX:CMSFullGCsBeforeCompaction=N 设定多少次CMS后,进行一次内存碎片整理
  • -XX:+CMSParallelRemarkEnabled 降低标记停顿

指定垃圾回收设置

#将参数加入到bin/catalina.sh中,重启观察Tomcat服务。老年代已经使用CMS
[root@centos7 ~]#vim /usr/local/tomcat/bin/catalina.sh
# OS specific support.  $var _must_ be set to either true or false.
#添加下面一行
JAVA_OPTS="-server -Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5"

cygwin=false
darwin=false

#重启Tomcat服务
[root@centos7 ~]#systemctl restart tomcat.service

#确认参数设置
[root@centos7 ~]#ps aux|grep tomcat
tomcat     2214 40.0  5.8 3057244 118992 ?      Sl   11:08   0:16 /usr/local/jdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
root       2248  0.0  0.0 112808   968 pts/0    S+   11:09   0:00 grep --color=auto tomcat

1.2.8 JAVA参数总结
参数名称含义默认值描述
-Xms初始堆大小物理内存的1/64(<1GB)默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制
-Xmx最大堆大小物理内存的1/4(<1GB)默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn年轻代大小(1.4or lator)注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的Newgen是不同的。 整个堆大小=年轻代大小 + 年老代大小 +持久代大小. 增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize年轻代最大值(for 1.3/1.4)
-XX:PermSize设置持久代(perm gen)初始值物理内存的1/64
-XX:MaxPermSize设置持久代最大值物理内存的1/4
-Xss每个线程的堆栈大小JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用,如果栈不是很深,应该是128k够用的。大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
参数名称含义默认值描述
-XX:ThreadStackSize线程堆栈大小0表示使用默认堆栈大小[Sparc:512;Solaris] x86:320;Sparc 64位:1024;Linux amd64:1024
-XX:NewRatio年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatioEden区与Survivor区的大小比值设置为8,则两个Survivor区,与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes内存页的大小不可设置过大, 会影响Perm的大小=128m
-XX:+UseFastAccessorMethods原始类型的快速优化
-XX:+DisableExplicitGC关闭System.gc()这个参数需要严格的测试
-XX:MaxTenuringThreshold垃圾最大年龄如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年轻代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加再年轻代即被回收的概率。该参数只有在串行GC时才有效
-XX:+AggressiveOpts加快编译
-XX:+UseBiasedLocking锁机制的性能改善
-Xnoclassgc禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB每兆堆空闲空间中SoftReference的存活时间可达的对象在上次被引用后将保留一段时间。 缺省值是堆中每个空闲兆字节的生命周期的一秒钟
-XX:PretenureSizeThreshold对象超过多大是直接在旧生代分配0单位字节。新生代采用Parallel Scavenge GC时无效。另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象。
-XX:TLABWasteTargetPercentTLAB占eden区的百分比1%
-XX:+CollectGen0FirstFullGC时是否先YGCfalse

并行收集器相关参数

参数名称含义默认值描述
-XX:+UseParallelGCFull GC采用parallel MSC选择垃圾收集器为并行收集器,此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
-XX:+UseParNewGC设置年轻代为并行收集可与CMS收集同时使用。 JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads并行收集器的线程数此值最好配置与处理器数目相等。 同样适用于CMS
-XX:+UseParallelOldGC年老代垃圾收集方式为并行收集(Parallel Compacting)这个是JAVA 6出现的参数选项
-XX:MaxGCPauseMillis每次年轻代垃圾回收的最长时间(最大暂停时间)如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
-XX:+UseAdaptiveSizePolicy自动选择年轻代区大小和相应的Survivor区比例设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等。此值建议使用并行收集器时,一直打开。
-XX:GCTimeRatio设置垃圾回收时间占程序运行时间的百分比公式为1/(1+n)
-XX:+ScavengeBeforeFullGCFull GC前调用YGCtrue

CMS相关参数

参数名称含义默认值描述
-XX:+UseConcMarkSweepGC使用CMS内存收集测试中配置这个以后,-XX:NewRatio=4的配置可能失效,所以,此时年轻代大小最好用-Xmn设置
-XX:+AggressiveHeap试图使用大量的物理内存。 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要256MB内存。
-XX:CMSFullGCsBeforeCompaction多少次后进行内存压缩由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生"碎片",使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩,整理。
-XX:+CMSParallelRemarkEnabled降低标记停顿
-XX+UseCMSCompactAtFullCollection在FULLGC的时候,对老年代的压缩CMS是不会移动内存的,因此,这个非常容易产生碎片,导致内存不够用。 因此,内存的压缩这个时候就会被启用。增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly使用手动定义初始化定义开始CMS收集禁止hostspot自行触发CMS GC
-XX:CMSInitiatingOccupancyFraction=70使用cms作为垃圾回收,使用70%后开始CMS收集92
-XX:CMSInitiatingPermOccupancyFraction设置PermGen使用到达多少比率时触发92
-XX:+CMSIncrementalMode设置为增量模式用于单CPU情况

1.3 Tomcat 性能优化常用配置

1.3.1 内存空间优化
JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "

-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值

生产案例:

[root@centos7 ~]#vim /usr/local/tomcat/bin/catalina.sh
# OS specific support.  $var _must_ be set to either true or false.
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"

cygwin=false
darwin=false
#一台tomcat服务器并发连接数不高,生产环境建议分配物理内存通常4G到8G较多,如果需要更多连接,一般会利用虚拟化技术实现多台tomcat

1.3.2 线程池调整
[root@centos7 ~]#vim /usr/local/tomcat/conf/server.xml
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

常用属性:

  • connectionTimeout :连接超时时长,单位ms
  • maxThreads:最大线程数,默认200
  • minSpareThreads:最小空闲线程数
  • maxSpareThreads:最大空闲线程数
  • acceptCount:当启动线程满了之后,等待队列的最大长度,默认100
  • URIEncoding:URI 地址编码格式,建议使用 UTF-8
  • enableLookups:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
  • compression:是否启用传输压缩机制,建议 “on”,CPU和流量的平衡
    • compressionMinSize:启用压缩传输的数据流最小值,单位是字节
    • compressableMimeType:定义启用压缩功能的MIME类型text/html,text/xml,text/css,text/javascript
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐