前言
Tomcat作为一个Java Servlet容器,用于运行Web应用程序,其性能和吞吐量对于确保应用程序的快速响应和高效运行至关重要。因此,Tomcat调优是为了最大限度地提高系统的性能和可扩展性,以满足高并发和大流量的需求。由于Tomcat的运行依赖于JVM,从虚拟机的角度把Tomcat的调整分为外部环境调优JVM和Tomcat自身调优两部分,当然操作系统(内核参数)也需要配合调整优化。
目录
[1. JVM组成](#1. JVM组成)
[1.1 JVM组成部分:](#1.1 JVM组成部分:)
[1.2 JVM运行时数据区域构成](#1.2 JVM运行时数据区域构成)
[1.3 虚拟机](#1.3 虚拟机)
[2. 垃圾回收](#2. 垃圾回收)
[2.1 Garbage 垃圾确定方法](#2.1 Garbage 垃圾确定方法)
[2.2 垃圾回收基本算法](#2.2 垃圾回收基本算法)
[2.2.1 标记-清除 Mark-Sweep](#2.2.1 标记-清除 Mark-Sweep)
[2.2.2 标记压缩 (压实)Mark-Compact](#2.2.2 标记压缩 (压实)Mark-Compact)
[2.2.3 复制 Copying](#2.2.3 复制 Copying)
[2.2.4 算法总结对比](#2.2.4 算法总结对比)
[2.2.5 STW](#2.2.5 STW)
[2.3 分代堆内存GC策略](#2.3 分代堆内存GC策略)
[2.3.1 堆内存分代](#2.3.1 堆内存分代)
[2.3.2 年轻代回收Minor GC](#2.3.2 年轻代回收Minor GC)
[2.3.3 老年代回收 Major GC](#2.3.3 老年代回收 Major GC)
[2.4 Java内存调整相关参数](#2.4 Java内存调整相关参数)
[2.4.1 JVM 内存常用相关参数](#2.4.1 JVM 内存常用相关参数)
[2.4.2 JVM参数设置](#2.4.2 JVM参数设置)
[二、Tomcat 配置文件参数优化](#二、Tomcat 配置文件参数优化)
[1. 常用的优化相关参数](#1. 常用的优化相关参数)
[2. 编辑全局配置](#2. 编辑全局配置)
一、JVM调优
1. JVM组成
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所用
1.2 JVM运行时数据区域构成
- Method Area (线程共享):方法区是所有线程共享的内存空间,存放已加载的类信息(构造方法,接口定义),常量(final),静态变量(static), 运行时常量池等。但实例变量存放在堆内存中. 从JDK8开始此空间由永久代改名为元空间
- heap (线程共享):堆在虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出OOM异常.堆是靠GC垃圾回收器管理的,通过-Xmx -Xms 指定最大堆和最小堆空间大小
- Java stack (线程私有):Java栈是每个线程会分配一个栈,存放java中8大基本数据类型,对象引用,实例的本地变量,方法参数和返回值等,基于FILO()(First In Last Out),每个方法为一个栈帧 1 50 %
- Program Counter Register (线程私有):PC寄存器就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令.因为线程需要切换,当一个线程被切换回来需要执行的时候,知道执行到哪里了
- Native Method stack (线程私有):本地方法栈为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。
内存区域图示:
1.3 虚拟机
JVM(Java Virtual Machine)是Java虚拟机的缩写,是Java平台的核心组成部分之一。JVM是一个虚拟计算机,它提供了一个安全的、可移植的运行环境,使得Java程序可以在不同的操作系统和硬件平台上运行。
2. 垃圾回收
垃圾回收是JVM的一个重要特性,它负责回收不再使用的Java对象,以释放内存空间。JVM中的垃圾回收器会定期扫描堆内存中的对象,标记那些仍然被引用的对象,并回收那些没有被引用的对象。垃圾回收器可以自动管理内存,使得Java程序员不需要手动释放内存,从而减少了内存泄漏和内存溢出的风险。
2.1 Garbage 垃圾确定方法
- 引用计数法:这种方法是通过给每个对象维护一个引用计数器,记录该对象被引用的次数,当引用计数器为0时,就可以判断该对象可以被回收。但是,这种方法无法解决循环引用的问题,即两个或多个对象相互引用,导致它们的引用计数器永远不为0,从而无法被回收。
- 可达性分析法:这种方法是通过从一组称为"GC Roots"的对象开始,查找所有被这些对象直接或间接引用的对象,如果一个对象没有被GC Roots引用,则可以判断该对象可以被回收。在Java中,GC Roots包括虚拟机栈中引用的对象、方法区中静态变量引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象等等。
2.2 垃圾回收基本算法
2.2.1 标记-清除 Mark-Sweep
分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,对未标记对象(即不再使用的对象)逐一进行清理。
注意:标记-清除最大的问题会造成内存碎片,但是不浪费空间,效率较高(如果对象较多,逐一删除效率也会影响)
2.2.2 标记压缩 (压实)Mark-Compact
分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。
注意:标记-压缩算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。缺点是内存整理过程有消耗,效率相对低下
2.2.3 复制 Copying
先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如先用A区域。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。缺点是比较浪费内存,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。好处是没有碎片,复制过程中保证对象使用连续空间,且一次性清除所有垃圾,所以效率很高。
2.2.4 算法总结对比
在不同场景按需选择最合适的算法
- 效率: 复制算法>标记清除算法> 标记压缩算法
- 内存整齐度: 复制算法=标记压缩算法> 标记清除算法
- 内存利用率: 标记压缩算法=标记清除算法>复制算法
2.2.5 STW
在JVM中,垃圾回收器会定期扫描堆内存中的对象,标记那些仍然被引用的对象,并回收那些没有被引用的对象。垃圾回收的过程中,会发生一种称为"Stop-The-World"(STW)的现象,即垃圾回收器会暂停应用程序的执行,以便进行垃圾回收。STW是垃圾回收器的一个缺点,因为它会导致应用程序的停顿时间变长,影响应用程序的性能和响应性。为了减少STW对应用程序的影响,JVM中的垃圾回收器采用了一些优化策略。
2.3 分代堆内存GC策略
2.3.1 堆内存分代
为了减少STW对应用程序的影响,JVM中的垃圾回收器采用了一些优化策略。分代垃圾回收策略将堆内存分为新生代、老年代和永久代三个部分,采用不同的垃圾回收算法进行处理,以减少垃圾回收的时间和空间开销,提高应用程序的性能和响应性。在实际应用中,我们需要根据应用程序的特点和需求选择合适的垃圾回收策略,并进行适当的调优,以获得更好的性能和稳定性。
说明:
在JVM中,新生代内存被分为三个部分:Eden空间、From Survivor空间和To Survivor空间。这三个空间的作用如下:
- Eden空间:是新创建对象的初始分配区域。当一个对象被创建时,它会被分配到Eden空间中。在Minor GC时,Eden空间中的存活对象会被复制到Survivor空间中,并清空Eden空间。
- From Survivor空间和To Survivor空间:是用于存储在垃圾回收过程中存活的对象的。在Minor GC时,Eden空间中的存活对象会被复制到To Survivor空间中。在下一次Minor GC时,To Survivor空间中的存活对象会被复制到From Survivor空间中,同时将Eden空间和From Survivor空间中的对象清空。在这个过程中,垃圾回收器会根据对象的存活时间和大小等因素,将对象放到不同的Survivor空间中。
Heap堆内存分为:
- 年轻代Young:Young Generation
- 老年代Tenured:Old Generation, 长时间存活的对象
2.3.2 年轻代回收Minor GC
Eden空间满时,会触发一次Minor GC(也称为Young GC),在这个过程中,垃圾回收器会扫描Eden空间和From Survivor空间中的对象,标记那些仍然被引用的对象,并将它们复制到To Survivor空间中。同时,垃圾回收器会清空Eden空间和From Survivor空间中的对象,以便下一次的对象分配。在Minor GC后,To Survivor空间变成了From Survivor空间,From Survivor空间变成了To Survivor空间。
① 标记存活对象:垃圾回收器会从根对象开始,标记所有在Eden空间和From Survivor空间中仍然被引用的对象。
② 复制存活对象:垃圾回收器会将标记为存活的对象复制到To Survivor空间中,并按照对象的年龄将对象放入相应的Survivor空间中。
③ 清空非存活对象:垃圾回收器会清空Eden空间和From Survivor空间中的非存活对象。
④ 空间交换:在Minor GC后,To Survivor空间变成了From Survivor空间,From Survivor空间变成了To Survivor空间。
通常场景下,大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。但是,如果一个对象一直存活,它最后就在from、to来回复制,如果from区中对象复制次数达到阈值(默认15次,CMS为6次,可通过java的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到老年代。
2.3.3 老年代回收 Major GC
老年代内存主要用于存放存活时间较长的对象。当老年代中的对象达到一定的数量时,会触发一次Major GC(也称为Full GC),在这个过程中,垃圾回收器会扫描整个堆内存中的对象,标记那些仍然被引用的对象,并将没有被引用的对象清除掉。
① 标记存活对象:垃圾回收器会从根对象开始,标记所有在堆内存中仍然被引用的对象。
② 清理非存活对象:垃圾回收器会清除所有没有被标记为存活的对象,并将空间释放出来。
③ 整理堆内存:垃圾回收器会将存活的对象移动到堆内存的一端,以便后续的对象分配。
④ 更新引用:垃圾回收器会更新所有存活对象的引用,使其指向正确的内存地址。
2.4 Java内存调整相关参数
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=2new/old=1/2 |
| -XX:SurvivorRatio | 以比例方式设置eden和survivor(S0或S1) | -XX:SurvivorRatio=6eden/survivor=6/1new/survivor=8/1 |
| -Xss | 设置每个线程私有的栈空间大小,依据具体线程 | -Xss256k |
标准选项:
bash
[root@localhost ~]#java
用法: java [-options] class [args...]
(执行类)
或 java [-options] -jar jarfile [args...]
(执行 jar 文件)
其中选项包括:
-d32 使用 32 位数据模型 (如果可用)
-d64 使用 64 位数据模型 (如果可用)
-server 选择 "server" VM
默认 VM 是 server,
因为您是在服务器类计算机上运行。
-cp <目录和 zip/jar 文件的类搜索路径>
-classpath <目录和 zip/jar 文件的类搜索路径>
用 : 分隔的目录, JAR 档案
和 ZIP 档案列表, 用于搜索类文件。
-D<名称>=<值>
设置系统属性
-verbose:[class|gc|jni]
启用详细输出
-version 输出产品版本并退出
-version:<值>
警告: 此功能已过时, 将在
未来发行版中删除。
需要指定的版本才能运行
-showversion 输出产品版本并继续
-jre-restrict-search | -no-jre-restrict-search
警告: 此功能已过时, 将在
未来发行版中删除。
在版本搜索中包括/排除用户专用 JRE
-? -help 输出此帮助消息
-X 输出非标准选项的帮助
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
按指定的粒度启用断言
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
禁用具有指定粒度的断言
-esa | -enablesystemassertions
启用系统断言
-dsa | -disablesystemassertions
禁用系统断言
-agentlib:<libname>[=<选项>]
加载本机代理库 <libname>, 例如 -agentlib:hprof
另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<选项>]
按完整路径名加载本机代理库
-javaagent:<jarpath>[=<选项>]
加载 Java 编程语言代理, 请参阅 java.lang.instrument
-splash:<imagepath>
使用指定的图像显示启动屏幕
非标准的稳定选项:
bash
[root@localhost ~]#java -X
-Xmixed 混合模式执行 (默认)
-Xint 仅解释模式执行
-Xbootclasspath:<用 : 分隔的目录和 zip/jar 文件>
设置搜索路径以引导类和资源
-Xbootclasspath/a:<用 : 分隔的目录和 zip/jar 文件>
附加在引导类路径末尾
-Xbootclasspath/p:<用 : 分隔的目录和 zip/jar 文件>
置于引导类路径之前
-Xdiag 显示附加诊断消息
-Xnoclassgc 禁用类垃圾收集
-Xincgc 启用增量垃圾收集
-Xloggc:<file> 将 GC 状态记录在文件中 (带时间戳)
-Xbatch 禁用后台编译
-Xms<size> 设置初始 Java 堆大小
-Xmx<size> 设置最大 Java 堆大小
-Xss<size> 设置 Java 线程堆栈大小
-Xprof 输出 cpu 配置文件数据
-Xfuture 启用最严格的检查, 预期将来的默认值
-Xrs 减少 Java/VM 对操作系统信号的使用 (请参阅文档)
-Xcheck:jni 对 JNI 函数执行其他检查
-Xshare:off 不尝试使用共享类数据
-Xshare:auto 在可能的情况下使用共享类数据 (默认)
-Xshare:on 要求使用共享类数据, 否则将失败。
-XshowSettings 显示所有设置并继续
-XshowSettings:all
显示所有设置并继续
-XshowSettings:vm 显示所有与 vm 相关的设置并继续
-XshowSettings:properties
显示所有属性设置并继续
-XshowSettings:locale
显示所有与区域设置相关的设置并继续
-X 选项是非标准选项, 如有更改, 恕不另行通知。
有不稳定选项的当前生效值:
bash
[root@centos7 ~]#java -XX:+PrintFlagsFinal
查看所有不稳定选项的默认值:
bash
[root@centos7 ~]#java -XX:+PrintFlagsInitial
2.4.2 JVM参数设置
默认的内存池空间大小:
① 查看默认状态页信息
② 修改现有内存池空间大小
bash
[root@localhost ~]# vim /usr/local/tomcat/bin/catalina.sh
119 JAVA_OPTS="-server -Xms512m -Xmx512m -XX:NewSize=100m -XX:MaxNewSize=200m"
[root@localhost ~]# systemctl restart tomcat.service
-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值
③ 再次查看状态页信息
二、Tomcat 配置文件参数优化
1. 常用的优化相关参数
bash
【redirectPort】如果某连接器支持的协议是HTTP,当接收客户端发来的HTTPS 443 请求时,则转发至此属性定义的 8443 端口。
【maxThreads】Tomcat使用线程来处理接收的每个请求,这个值表示Tomcat可创建的最大的线程数,即支持的最大并发连接数,默认值是 200。
【minSpareThreads】最小空闲线程数,Tomcat 启动时的初始化的线程数,表示即使没有人使用也开这么多空线程等待,默认值是 10。
【maxSpareThreads】最大备用线程数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值是-1(无限制)。一般不需要指定。
【processorCache】进程缓冲器,可以提升并发请求。默认值是200,如果不做限制的话可以设置为-1,一般采用maxThreads的值或者-1。
【URIEncoding】指定 Tomcat 容器的 URL 编码格式,网站一般采用UTF-8作为默认编码。
【connnectionTimeout】网络连接超时,单位:毫秒,设置为 0 表示永不超时,这样设置有隐患的。通常默认 20000 毫秒就可以。
【enableLookups】是否反查域名,以返回远程主机的主机名,取值为:true 或 false,如果设置为 false,则直接返回 IP 地址,为了提高处理能力,应设置为 false。
【disableUploadTimeout】上传时是否使用超时机制。应设置为 true。
【connectionUploadTimeout】上传超时时间,毕竟文件上传可能需要消耗更多的时间,这个根据你自己的业务需要自己调,以使Servlet有较长的时间来完成它的执行,需要与上一个参数一起配合使用才会生效。
【acceptCount】指定当所有可以使用的处理请求的线程数都被使用时,可传入连接请求的最大队列长度,超过这个数的请求将不予处理,默认为 100 个。
【maxKeepAliveRequests】指定一个长连接的最大请求数。默认长连接是打开的,设置为1时,代表关闭长连接;为-1时,代表请求数无限制
【compression】是否对响应的数据进行GZIP压缩,off:表示禁止压缩;on:表示允许压缩(文本将被压缩)、force:表示所有情况下都进行压缩,默认值为 off,压缩数据后可以有效的减少页面的大小,一般可以减小 1/3 左右,节省带宽。
【compressionMinSize】表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是 2048。
【compressableMimeType】压缩类型,指定对哪些类型的文件进行数据压缩。
【noCompressionUserAgents="gozilla, traviata"】对于以下的浏览器,不启用压缩
#如果已经进行了动静分离处理,静态页面和图片等数据就不需做 Tomcat 处理,也就不要在 Tomcat 中配置压缩了。
2. 编辑全局配置
bash
vim /usr/local/tomcat/conf/server.xml
......
<Connector port="8080" protocol="HTTP/11.1"
connectionTimeout="20000"
redirectPort="8443"
--71行--插入
minSpareThreads="50"
enableLookups="false"
disableUploadTimeout="true"
acceptCount="300"
maxThreads="500"
processorCache="500"
URIEncoding="UTF-8"
maxKeepAliveRequests="100"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image /jpg,image/png"/>