NO1. 提高Tomcat性能一般从三方面入手,JVM内存调整、禁用DNS查询、调整线程数。

一、JVM内存调整   -Xms<size> 表示JVM初始化堆的大小,-Xmx<size>表示JVM堆的最大值。这两个值的大小一般根据需要进行设置。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。在catalina.bat中,设置JAVA_OPTS='-Xms256m -Xmx512m',表示初始化内存为256MB,可以使用的最大内存为512MB。  二、禁用DNS查询   当web应用程序向要记录客户端的信息时,它也会记录客户端的IP地址或者通过域名服务器查找机器名转换为IP地址。DNS查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程,这样会消耗一定的时间。为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值 enableLookups="false" 三、调整线程数   另外一个可通过应用程序的连接器(Connector)进行性能控制的的参数是创建的处理请求的线程数。Tomcat使用线程池加速响应速度来处理请求。在Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。

NO2. 浅入Tomcat 运行占用内存

一、Tomcat内存使用   Tomcat的运行方式部分取决于所运行的Java虚拟机的配置信息。如果没有将JVM配置成可以使用指定堆栈的数据上限,则它只用默认内存数量的上限,这可能不足以让其运行web应用程序。   整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。   Java应用每创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在TOMCAT余下的内存里创建这个物理线程,而不是在JVM的Xmx设置的内存堆里创建。-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。结论:要想创建更多的线程,必须减少分配给JVM的最大内存。   创建线程的最大个数的估算公式:   (MaxProcessMemory-JVMMemory-ReservedOsMemory)/(ThreadStackSize)=Number of threads   MaxProcessMemory:指的是一个进程的最大内存   JVMMemory:JVM内存   ReservedOsMemory:保留的操作系统内存   ThreadStackSize:线程栈的大小        (8G-2G-128M)/1M = ~6000 threads【64为系统线程最大内存无限制,但与机器上其他服务有关】   线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。  二、Tomcat运行占用内存   Tomcat运行占用内存= Xmx占用的内存 + Perm Generation(永久保存区域)占用内存 + 所有Java应用创建线程数x 1M。        1). Young Generation(年轻代):用于存放“早逝”对象(即瞬时对象)。例如:在创建对象时或者调用方法时使用的临时对象或局部变量。   年轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。        2). Tenured Generation(年老代):用于存放“驻留”对象(即较长时间被引用的对象)。往往体现为一个大型程序中的全局对象或长时间被使用的对象。        3). Perm Generation(永久保存区域):用于存放“永久”对象。这些对象管理着运行于JVM中的类和方法。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。 三、顺便提及一下在JVM中两种垃圾回收方式:        1). 一种叫做Minor(次收集)。Minor在YoungGeneration(年轻代)的空间被对象全部占用后执行,主要是对YoungGeneration中的对象进行垃圾收集。        2). 一种叫做Major(主收集)。Major是针对于整个Heap size(Xms和Xmx设置为JVM使用的内存,但不包括永久保存区域使用的内存)的垃圾收集。        其中Minor方式的收集经常发生,并且Minor收集所占用的系统时间小。而Major方式的垃圾收集则是一种“昂贵”的垃圾收集方式,因为在Major要对整个Heap size进行垃圾收集,这会使得应用停顿的时间变得较长。

NO3. Tomcat内存调整(JVM内存调整)

Tomcat内存优化主要是对 tomcat 启动参数优化,我们可以在 tomcat 的启动脚本 catalina.sh 中设置 JAVA_OPTS参数。
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8
-server -Xms1024m -Xmx2048m
-XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m
-XX:MaxPermSize=256m -XX:+DisableExplicitGC"
用途
JVM选项
含义
内存设置-Xms1024M设置JVM启动时堆栈内存的大小
内存设置-Xmx2048M设置JVM可扩展的最大堆栈内存
内存设置-XX:PermSize=256M设置非堆内存初始值,默认是物理内存的1/64
内存设置-XX:MaxPermSize=256M设置最大非堆内存的大小,默认是物理内存的1/4
调试安全性-Djava.security.debug=all出于安全性,打开所有调试输出
调试-enableassertions启用assertion检查
调试-verbose:class启用verbose class加载调试输出
调试-verbose:gc启用verbose garbage collection调试输出
图形显示-Djava.awt.headless=true

允许在没有图形显示所安装的软件的情况下运行JVM。有时我们会在J2EE工程中使用一些图表工具如:jfreechart,用于在web网页输出GIF/JPG等流,在winodws环境下,一般在输出图形时不会碰到什么问题,但是在linux/unix环境下经常会碰到一个exception,因此加上这个参数以免避这样的情况出现。

本地化-Duser.language=en设置Tomcat使用的绑定语言
本地化-Dfile.encoding=UTF-8设置Tomcat使用的默认文件编码规则
操作-XX:+DisableExplicitGC

标志自动将System.gc()调用转换成一个空操作,就是应用中调用System.gc() 会变成一个空操作。最主要的原因是为了防止某些手贱的同学在代码里到处写 System.gc()的调用而干扰了程序的正常运行。

NO4. Tomcat并发优化(调整线程数量)

一. Tomcat连接相关参数在Tomcat 配置文件 server.xml 中的 <Connector ... /> 配置中参数说明
参数
含义
minProcessors最小空闲连接线程数,用于提高系统处理性能,默认值为 10
maxProcessors最大连接线程数,即:并发处理的最大请求数,默认值为 75
acceptCount允许的最大连接数,应大于等于 maxProcessors ,默认值为 100
enableLookups是否反查域名,取值为: true 或 false 。为了提高处理能力,应设置为 false
connectionTimeout网络连接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为30000 毫秒
其中和最大连接数相关的参数为maxProcessors 和 acceptCount 。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常 Windows 是 2000 个左右, Linux 是1000 个左右。二. 调整连接器connector的并发处理能力参数说明
参数
含义
maxThreads客户请求最大线程数
minSpareThreadsTomcat初始化时创建的 socket 线程数
maxSpareThreadsTomcat连接器的最大空闲 socket 线程数
enableLookups若设为true, 则支持域名解析,可把 ip 地址解析为主机名
redirectPort在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口
acceptAccount监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads )
connectionTimeout连接超时
minProcessors服务器创建时的最小处理线程数
maxProcessors服务器同时最大处理线程数
URIEncoding URL统一编码

NO5. Tomcat缓存优化

参数说明
参数
含义
compression 打开压缩功能
compressionMinSize启用压缩的输出内容大小,这里面默认为2KB
compressableMimeType压缩类型
connectionTimeout定义建立客户连接超时的时间. 如果为 -1, 表示不限制建立客户连接的时间

NO6. 实例说明

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8
-server -Xms1024m -Xmx2048m
-XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m
-XX:MaxPermSize=256m -XX:+DisableExplicitGC"
配置完成后可重启Tomcat ,通过以下命令进行查看配置是否生效:sudo lsof -i:Tomcat端口号
[ligang@web01 bin]$ lsof -i:8081
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 19404 ligang 34u IPv4 3386023804 0t0 TCP *:tproxy (LISTEN)
sudo jmap - heap Tomcat进程号
[ligang@web01 bin]$ jmap -heap 19404
Attaching to process ID 19404, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b11
using thread-local object allocation.
Parallel GC with 13 thread(s)
Heap Configuration:
 MinHeapFreeRatio = 40
 MaxHeapFreeRatio = 70
 MaxHeapSize = 2147483648 (2048.0MB)
 NewSize = 268435456 (256.0MB)
 MaxNewSize = 268435456 (256.0MB)
 OldSize = 5439488 (5.1875MB)
 NewRatio = 2
 SurvivorRatio = 8
 PermSize = 268435456 (256.0MB)
 MaxPermSize = 268435456 (256.0MB)
Heap Usage:
PS Young Generation
Eden Space:
 capacity = 186384384 (177.75MB)
 used = 128285416 (122.3425064086914MB)
 free = 58098968 (55.407493591308594MB)
 68.82841429462245% used
From Space:
 capacity = 41025536 (39.125MB)
 used = 5296920 (5.051536560058594MB)
 free = 35728616 (34.073463439941406MB)
 12.911275552865416% used
To Space:
 capacity = 40173568 (38.3125MB)
 used = 0 (0.0MB)
 free = 40173568 (38.3125MB)
 0.0% used
PS Old Generation
 capacity = 1879048192 (1792.0MB)
 used = 891961888 (850.6411437988281MB)
 free = 987086304 (941.3588562011719MB)
 47.4688138280596% used
PS Perm Generation
 capacity = 268435456 (256.0MB)
 used = 46790248 (44.622657775878906MB)
 free = 221645208 (211.3773422241211MB)
 17.430725693702698% used
jstat -gcutil pid
S0
S1
E
O
P
YGC
YGCT
FGC
FGCT
GCT
19.740.0050.9340.4716.851221.73900.0001.739
参数含义: S0:Heap上的 Survivor space 0 段已使用空间的百分比 S1:Heap上的 Survivor space 1 段已使用空间的百分比 E: Heap上的 Eden space 段已使用空间的百分比 O: Heap上的 Old space 段已使用空间的百分比 P: Perm space 已使用空间的百分比 YGC:从程序启动到采样时发生Young GC的次数 YGCT:Young GC所用的时间(单位秒) FGC:从程序启动到采样时发生Full GC的次数 FGCT:Full GC所用的时间(单位秒) GCT:用于垃圾回收的总时间(单位秒)
<Connector port="8081" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
 enableLookups="false" redirectPort="9443" server="xxx" acceptCount="100"
 debug="0" connectionTimeout="20000" maxPostSize="0"
 disableUploadTimeout="true" URIEncoding="utf-8" useBodyEncodingForURI="true" />
<Connector server="ligang" port="9443" minProcessors="5" maxProcessors="150" minSpareThreads="25" maxSpareThreads="75"
 disableUploadTimeout="true" URIEncoding="utf-8" useBodyEncodingForURI="true" SSLEnabled="true"
 clientAuth="false" keystoreFile="wild_ligang.p12" keystorePass="ligang" keystoreType="PKCS12" sslProtocol="TLS"
 enableLookups="true" connectionTimeout="20000" acceptCount="100" debug="0" scheme="https" secure="true" />

NO7. 其他说明

一. 查看系统位数:uname -ax86_64说明是64位内核, 跑的是64位的系统.i386, i686说明是32位的内核, 跑的是32位的系统二. 查看内存使用情况:free
total 
used 
free 
shared 
buffers 
cached
Mem: 80588686196412 1862456 085968 1327960
-/+ buffers/cache:4782484 3276384   
Swap: 2097144 1300372 796772   
total:总计物理内存的大小used:已使用多大free:可用多大shared:多个进程共享的内存总额buffers/cached:缓存情况三. 注意事项:
  1. 设置NewSize、MaxNewSize相等,"new"的大小最好不要大于"old" 的一半,原因是old区如果不够大会频繁的触发主GC,大大降低了性能。
  2. 线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。
  3. -XX:PermSize设置最小分配空间,-XX:MaxPermSize设置最大分配空间。一般把这两个数值设为相同,以减少申请内存空间的时间。
  4. 当tomcat线程数增大,但没有超过最大线程数时,平均响应时间会增大,但这不意味着tomcat在线程增多时响应速度变慢.在测试中发现,由于线程数增多,在请求时需要排队,导致一部分请求会一直排队,随着线程数的增多,排队等待的最大时间也会越来越大,而这些大数据才是导致平均响应时间变大的罪魁祸首.响应时间大于3s的请求占总请求的比例一直维持在3%-5%。
  5. 因为对于操作系统,请求内存的系统调用会占用大量的cpu时间,所以频繁的请求、释放内存将会导致性能的严重下降。所以对于jvm,最好的方式就是尽量多占用内存作为heap,少释放甚至不释放空闲的heap给操作系统以减少消耗在内存请求、释放操作上的cpu时间。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐