0%

tomcat配置优化参数

1
2
export LD_LIBRARY_PATH=/home/opt/tools/apr/lib
JAVA_OPTS="-server -XX:PermSize=128M -XX:MaxPermSize=256m -Xms1g -Xmx1g -Xmn500m -XX:SurvivorRatio=65536 -XX:MaxTenuringThreshold=0 -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -Duser.timezone=Asia/Shanghai"

Tomcat 启动行参数的优化

Tomcat 的启动参数位于 tomcat 的安装目录\bin 目录下,如果你是 Linux 操作系统就是 catalina.sh 文件,如果你是 Windows 操作系统那么你需要改动的就是 catalina.bat 文件。打开该文件,一般该文件头部是一堆的由##包裹着的注释文字,找到注释文字的最后一段如:

1
2
3
4
# $Id: catalina.sh 522797 2007-03-27 07:10:29Z fhanik $
# -----------------------------------------------------------------------------

# OS specific support. $var _must_ be set to either true or false.

敲入一个回车,加入如下的参数

  • Linux 系统中 tomcat 的启动参数
1
export JAVA_OPTS="-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true "
  • Windows 系统中 tomcat 的启动参数
1
set JAVA_OPTS=-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true

上面参数好多啊,可能有人写到现在都没见一个 tomcat 的启动命令里加了这么多参数,当然,这些参数只是我机器上的,不一定适合你,尤其是参数后的 value(值)是需要根据你自己的实际情况来设置的。

参数解释:

  • -server

我不管你什么理由,只要你的 tomcat 是运行在生产环境中的,这个参数必须给我加上

因为 tomcat 默认是以一种叫 java –client 的模式来运行的,server 即意味着你的 tomcat 是以真实的 production 的模式在运行的,这也就意味着你的 tomcat 以 server 模式运行时将拥有:更大、更高的并发处理能力,更快更强捷的 JVM 垃圾回收机制,可以获得更多的负载与吞吐量。。。更。。。还有更。。。

Y 给我记住啊,要不然这个-server 都不加,那是要打屁股了。

  • -Xms–Xmx

即 JVM 内存设置了,把 Xms 与 Xmx 两个值设成一样是最优的做法,有人说 Xms 为最小值,Xmx 为最大值不是挺好的,这样设置还比较人性化,科学化。人性?科学?你个头啊。

大家想一下这样的场景:

一个系统随着并发数越来越高,它的内存使用情况逐步上升,上升到最高点不能上升了,开始回落,你们不要认为这个回落就是好事情,由其是大起大落,在内存回落时它付出的代价是 CPU 高速开始运转进行垃圾回收,此时严重的甚至会造成你的系统出现“卡壳”就是你在好好的操作,突然网页像死在那边一样几秒甚至十几秒时间,因为 JVM 正在进行垃圾回收。

因此一开始我们就把这两个设成一样,使得 Tomcat 在启动时就为最大化参数充分利用系统的效率,这个道理和 jdbcconnection pool 里的 minpool size 与 maxpool size 的需要设成一个数量是一样的原理。

如何知道我的 JVM 能够使用最大值啊?拍脑袋?不行!

在设这个最大内存即 Xmx 值时请先打开一个命令行,键入如下的命令:

tomcat

看,能够正常显示 JDK 的版本信息,说明,这个值你能够用。不是说 32 位系统下最高能够使用 2GB 内存吗?即:2048m,我们不防来试试

tomcat

可以吗?不可以!不要说 2048m 呢,我们小一点,试试 1700m 如何

tomcat

嘿嘿,连 1700m 都不可以,更不要说 2048m 了呢,2048m 只是一个理论数值,这样说吧我这边有几台机器,有的机器-Xmx1800 都没问题,有的机器最高只能到-Xmx1500m。

因此在设这个-Xms 与-Xmx 值时一定一定记得先这样测试一下,要不然直接加在 tomcat 启动命令行中你的 tomcat 就再也起不来了,要飞是飞不了,直接成了一只瘟猫了。

  • –Xmn

设置年轻代大小为 512m。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为 64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8。

  • -Xss

是指设定每个线程的堆栈大小。这个就要依据你的程序,看一个线程 大约需要占用多少内存,可能会有多少线程同时运行等。一般不易设置超过 1M,要不然容易出现 out ofmemory。

  • -XX:+AggressiveOpts

作用如其名(aggressive),启用这个参数,则每当 JDK 版本升级时,你的 JVM 都会使用最新加入的优化技术(如果有的话)

  • -XX:+UseBiasedLocking

启用一个优化了的线程锁,我们知道在我们的 appserver,每个 http 请求就是一个线程,有的请求短有的请求长,就会有请求排队的现象,甚至还会出现线程阻塞,这个优化了的线程锁使得你的 appserver 内对线程处理自动进行最优调配。

  • -XX:PermSize=128M-XX:MaxPermSize=256M

JVM 使用-XX:PermSize 设置非堆内存初始值,默认是物理内存的 1/64;

在数据量的很大的文件导出时,一定要把这两个值设置上,否则会出现内存溢出的错误。

由 XX:MaxPermSize 设置最大非堆内存的大小,默认是物理内存的 1/4。

那么,如果是物理内存 4GB,那么 64 分之一就是 64MB,这就是 PermSize 默认值,也就是永生代内存初始大小;

四分之一是 1024MB,这就是 MaxPermSize 默认大小。

  • -XX:+DisableExplicitGC

在程序代码中不允许有显示的调用”System.gc()”。看到过有两个极品工程中每次在 DAO 操作结束时手动调用 System.gc()一下,觉得这样做好像能够解决它们的 out ofmemory 问题一样,付出的代价就是系统响应时间严重降低,就和我在关于 Xms,Xmx 里的解释的原理一样,这样去调用 GC 导致系统的 JVM 大起大落,性能不到什么地方去哟!

  • -XX:+UseParNewGC

对年轻代采用多线程并行回收,这样收得快。

  • -XX:+UseConcMarkSweepGC

即 CMS gc,这一特性只有 jdk1.5 即后续版本才具有的功能,它使用的是 gc 估算触发和 heap 占用触发。

我们知道频频繁的 GC 会造面 JVM 的大起大落从而影响到系统的效率,因此使用了 CMS GC 后可以在 GC 次数增多的情况下,每次 GC 的响应时间却很短,比如说使用了 CMS GC 后经过 jprofiler 的观察,GC 被触发次数非常多,而每次 GC 耗时仅为几毫秒。

  • -XX:MaxTenuringThreshold

设置垃圾最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。

这个值的设置是根据本地的 jprofiler 监控后得到的一个理想的值,不能一概而论原搬照抄。

  • -XX:+CMSParallelRemarkEnabled

在使用 UseParNewGC 的情况下, 尽量减少 mark 的时间

  • -XX:+UseCMSCompactAtFullCollection

在使用 concurrent gc 的情况下, 防止 memoryfragmention, 对 live object 进行整理, 使 memory 碎片减少。

  • -XX:LargePageSizeInBytes

指定 Java heap 的分页页面大小

  • -XX:+UseFastAccessorMethods

get,set 方法转成本地代码

  • -XX:+UseCMSInitiatingOccupancyOnly

指示只有在 oldgeneration 在使用了初始化的比例后 concurrent collector 启动收集

  • -XX:CMSInitiatingOccupancyFraction=70

CMSInitiatingOccupancyFraction,这个参数设置有很大技巧,基本上满足(Xmx-Xmn)*(100- CMSInitiatingOccupancyFraction)/100>=Xmn 就不会出现 promotion failed。在我的应用中 Xmx 是 6000,Xmn 是 512,那么 Xmx-Xmn 是 5488 兆,也就是年老代有 5488 兆,CMSInitiatingOccupancyFraction=90 说明年老代到 90%满的时候开始执行对年老代的并发垃圾回收(CMS),这时还 剩 10%的空间是 5488*10%=548 兆,所以即使 Xmn(也就是年轻代共 512 兆)里所有对象都搬到年老代里,548 兆的空间也足够了,所以只要满 足上面的公式,就不会出现垃圾回收时的 promotion failed;

因此这个参数的设置必须与 Xmn 关联在一起。

  • -Djava.awt.headless=true

这个参数一般我们都是放在最后使用的,这全参数的作用是这样的,有时我们会在我们的 J2EE 工程中使用一些图表工具如:jfreechart,用于在 web 网页输出 GIF/JPG 等流,在 winodws 环境下,一般我们的 app server 在输出图形时不会碰到什么问题,但是在 linux/unix 环境下经常会碰到一个 exception 导致你在 winodws 开发环境下图片显示的好好可是在 linux/unix 下却显示不出来,因此加上这个参数以免避这样的情况出现。

上述这样的配置,基本上可以达到:

  • 系统响应时间增快

  • JVM 回收速度增快同时又不影响系统的响应率

  • JVM 内存最大化利用

  • 线程阻塞情况最小化

Tomcat 容器内的优化

前面我们对 Tomcat 启动时的命令进行了优化,增加了系统的 JVM 可使用数、垃圾回收效率与线程阻塞情况、增加了系统响应效率等还有一个很重要的指标,我们没有去做优化,就是吞吐量。

还记得我们在第三天的学习中说的,这个系统本身可以处理 1000,你没有优化和配置导致它默认只能处理 25。因此下面我们来看 Tomcat 容器内的优化。

打开 tomcat 安装目录\conf\server.xml 文件,定位到这一行:

1
<Connector port="8080" protocol="HTTP/1.1"

这一行就是我们的 tomcat 容器性能参数设置的地方,它一般都会有一个默认值,这些默认值是远远不够我们的使用的,我们来看经过更改后的这一段的配置:

1
2
3
4
5
6
7
8
9
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true" connectionTimeout="20000"
acceptCount="300" maxThreads="300" maxProcessors="1000" minProcessors="5"
useURIValidationHack="false"
compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
redirectPort="8443"
/>

好大一陀唉。。。。。。

没关系,一个个来解释

  • URIEncoding=”UTF-8”

使得 tomcat 可以解析含有中文名的文件的 url,真方便,不像 apache 里还有搞个 mod_encoding,还要手工编译

  • maxSpareThreads

maxSpareThreads 的意思就是如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。

  • minSpareThreads

最小备用线程数,tomcat 启动时的初始化的线程数。

  • enableLookups

这个功效和 Apache 中的 HostnameLookups 一样,设为关闭。

  • connectionTimeout

connectionTimeout 为网络连接超时时间毫秒数。

  • maxThreads

maxThreads Tomcat 使用线程来处理接收的每个请求。这个值表示 Tomcat 可创建的最大的线程数,即最大并发数。

*acceptCount

acceptCount 是当线程数达到 maxThreads 后,后续请求会被放入一个等待队列,这个 acceptCount 是这个队列的大小,如果这个队列也满了,就直接 refuse connection

  • maxProcessors 与 minProcessors

在 Java 中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出 CPU 最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。

通常 Windows 是 1000 个左右,Linux 是 2000 个左右。

  • useURIValidationHack

我们来看一下 tomcat 中的一段源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
security
if (connector.getUseURIValidationHack()) {
String uri = validate(request.getRequestURI());
if (uri == null) {
res.setStatus(400);
res.setMessage("Invalid URI");
throw new IOException("Invalid URI");
} else {
req.requestURI().setString(uri);
// Redoing the URI decoding
req.decodedURI().duplicate(req.requestURI());
req.getURLDecoder().convert(req.decodedURI(), true);
}
}

可以看到如果把 useURIValidationHack 设成”false”,可以减少它对一些 url 的不必要的检查从而减省开销。

  • enableLookups=”false”

为了消除 DNS 查询对性能的影响我们可以关闭 DNS 查询,方式是修改 server.xml 文件中的 enableLookups 参数值。

  • disableUploadTimeout

类似于 Apache 中的 keeyalive 一样

  • 给 Tomcat 配置 gzip 压缩(HTTP 压缩)功能
1
2
compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"

HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程 HTML,CSS,Javascript , Text ,它可以节省 40%左右的流量。更为重要的是,它可以对动态生成的,包括 CGI、PHP , JSP , ASP , Servlet,SHTML 等输出的网页也能进行压缩,压缩效率惊人。

  1. compression=”on” 打开压缩功能

  2. compressionMinSize=”2048” 启用压缩的输出内容大小,这里面默认为 2KB

  3. noCompressionUserAgents=”gozilla, traviata” 对于以下的浏览器,不启用压缩

  4. compressableMimeType=”text/html,text/xml” 压缩类型

最后不要忘了把 8443 端口的地方也加上同样的配置,因为如果我们走 https 协议的话,我们将会用到 8443 端口这个段的配置,对吧?

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--enable tomcat ssl-->
<Connector port="8443" protocol="HTTP/1.1"
URIEncoding="UTF-8" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true" connectionTimeout="20000"
acceptCount="300" maxThreads="300" maxProcessors="1000" minProcessors="5"
useURIValidationHack="false"
compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
SSLEnabled="true"
scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="d:/tomcat2/conf/shnlap93.jks" keystorePass="aaaaaa"
/>

好了,所有的 Tomcat 优化的地方都加上了。结合第三天中的 Apache 的性能优化,我们这个架构可以“飞奔”起来了,当然这边把有提及任何关于数据库优化的步骤,但仅凭这两步,我们的系统已经有了很大的提升。

举个真实的例子:上一个项目,经过 4 轮 performance testing,第一轮进行了问题的定位,第二轮就是进行了 apache+tomcat/weblogic 的优化,第三轮是做集群优化,第四轮是 sql 与 codes 的优化。

在到达第二轮时,我们的性能已经提升了多少倍呢?我们来看一个 loaderrunner 的截图吧:

tomcat

左边第一列是第一轮没有经过任何调优的压力测试报告。

右边这一列是经过了 apache 优化,tomcat 优化后得到的压力测试报告。

大家看看,这就提高了多少倍?这还只是在没有改动代码的情况下得到的改善,现在明白了好好的调优一个 apache 和 tomcat 其实是多么的重要了?如果加上后面的代码、SQL 的调优、数据库的调优。。。。。。所以我在上一个工程中有单笔交易性能(无论是吞吐量、响应时间)提高了 80 倍这样的极端例子的存在。