tomcat内存线程性能优化

tomcat默认参数是为开发环境制定,而非适合生产环境,尤其是内存和线程的配置,默认都很低,容易成为性能瓶颈。

tomcat内存优化

linux修改TOMCAT_HOME/bin/catalina.sh,在前面加入

JAVA_OPTS="-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m -Duser.timezone=Asia/Shanghai"

windows修改TOMCAT_HOME/bin/catalina.bat,在前面加入

set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m

最大堆内存是1024m,对于现在的硬件还是偏低,实施时,还是按照机器具体硬件配置优化。

tomcat 线程优化

编辑Tomcat目录下面的conf子目录下面的server.xml文件

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

acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。

connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

minSpareThreads:Tomcat初始化时创建的线程数。

maxSpareThreads:一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。

<Connector port="80" protocol="HTTP/1.1" maxThreads="600" minSpareThreads="100" maxSpareThreads="500" acceptCount="700"
connectionTimeout="20000" redirectPort="8443" />
maxThreads="600"       ///最大线程数
minSpareThreads="100"///初始化时创建的线程数
maxSpareThreads="500"///一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。
acceptCount="700"//指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理

这里是http connector的优化,如果使用apache和tomcat做集群的负载均衡,并且使用ajp协议做apache和tomcat的协议转发,那么还需要优化ajp connector。

<Connector port="8009" protocol="AJP/1.3" maxThreads="600" minSpareThreads="100" maxSpareThreads="500" acceptCount="700"
connectionTimeout="20000" redirectPort="8443" />

由于tomcat有多个connector,所以tomcat线程的配置,又支持多个connector共享一个线程池。

首先。打开/conf/server.xml,增加

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="20" maxIdleTime="60000" />

最大线程500(一般服务器足以),最小空闲线程数20,线程最大空闲时间60秒。

然后,修改<Connector ...>节点,增加executor属性,executor设置为线程池的名字:

<Connector executor="tomcatThreadPool" port="80" protocol="HTTP/1.1"  connectionTimeout="60000" keepAliveTimeout="15000" maxKeepAliveRequests="1"  redirectPort="443" />

可以多个connector公用1个线程池,所以ajp connector也同样可以设置使用tomcatThreadPool线程池。


Tomcat的四种基于HTTP协议的Connector性能比较

Tomcat从5.5版本开始,支持以下四种Connector的配置分别为:

<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/> 
<Connector executor="tomcatThreadPool" port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector executor="tomcatThreadPool" port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="8443" />

我们姑且把上面四种Connector按照顺序命名为 NIO, HTTP, POOL, NIOP

为了不让其他因素影响测试结果,我们只对一个很简单的jsp页面进行测试,这个页面仅仅是输出一个Hello World。假设地址是 http://tomcat1/test.jsp

我们依次对四种Connector进行测试,测试的客户端在另外一台机器上用ab命令来完成,测试命令为: ab -c 900 -n 2000 http://tomcat1/test.jsp ,最终的测试结果如下表所示(单位:平均每秒处理的请求数):

NIO    HTTP    POOL    NIOP

281    65          208       365

666    66          110       398

692    65          66         263

256    63          94         459

440    67          145       363

由这五组数据不难看出,HTTP的性能是很稳定,但是也是最差的,而这种方式就是Tomcat的默认配置。NIO方式波动很大,但没有低于280 的,NIOP是在NIO的基础上加入线程池,可能是程序处理更复杂了,因此性能不见得比NIO强;而POOL方式则波动很大,测试期间和HTTP方式一样,不时有停滞。

由于Linux的内核默认限制了最大打开文件数目是1024,因此此次并发数控制在900。

尽管这一个结果在实际的网站中因为各方面因素导致,可能差别没这么大,例如受限于数据库的性能等等的问题。但对我们在部署网站应用时还是具有参考价值的。


禁用DNS查询

当web应用程序向要记录客户端的信息时,它也会记录客户端的IP地址或者通过域名服务器查找机器名 转换为IP地址。

DNS查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程,这样会消耗一定的时间。

修改server.xml文件中的Connector元素,修改属性enableLookups参数值: enableLookups="false"

如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址

设置session过期时间

conf\web.xml中通过参数指定:

    <session-config>   
        <session-timeout>180</session-timeout>     
    </session-config>

单位为分钟。

Apr插件提高Tomcat性能

Tomcat可以使用APR来提供超强的可伸缩性和性能,更好地集成本地服务器技术.

APR(Apache Portable Runtime)是一个高可移植库,它是Apache HTTP Server 2.x的核心。APR有很多用途,包括访问高级IO功能(例如sendfile,epoll和OpenSSL),OS级别功能(随机数生成,系统状态等等),本地进程管理(共享内存,NT管道和UNIX sockets)。这些功能可以使Tomcat作为一个通常的前台WEB服务器,能更好地和其它本地web技术集成,总体上让Java更有效率作为一个高性能web服务器平台而不是简单作为后台容器。

在产品环境中,特别是直接使用Tomcat做WEB服务器的时候,应该使用Tomcat Native来提高其性能  

要测APR给tomcat带来的好处最好的方法是在慢速网络上(模拟Internet),将Tomcat线程数开到300以上的水平,然后模拟一大堆并发请求。

如果不配APR,基本上300个线程狠快就会用满,以后的请求就只好等待。但是配上APR之后,并发的线程数量明显下降,从原来的300可能会马上下降到只有几十,新的请求会毫无阻塞的进来。

在局域网环境测,就算是400个并发,也是一瞬间就处理/传输完毕,但是在真实的Internet环境下,页面处理时间只占0.1%都不到,绝大部分时间都用来页面传输。如果不用APR,一个线程同一时间只能处理一个用户,势必会造成阻塞。所以生产环境下用apr是非常必要的。

(1)安装APR tomcat-native

apr-1.3.8.tar.gz   安装在/usr/local/apr

#tar zxvf apr-1.3.8.tar.gz
#cd apr-1.3.8
#./configure
#make
#make install

apr-util-1.3.9.tar.gz  安装在/usr/local/apr/lib

#tar zxvf apr-util-1.3.9.tar.gz
#cd apr-util-1.3.9  
#./configure --with-apr=/usr/local/apr ----with-java-home=JDK
#make
#make install
#cd apache-tomcat-6.0.20/bin  
#tar zxvf tomcat-native.tar.gz  
#cd tomcat-native/jni/native  
#./configure --with-apr=/usr/local/apr
#make
#make install

设置apr环境变量


#vi /etcprofile
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib
source /etc/profile

(2)设置 Tomcat 整合 APR

修改 tomcat 的启动 shell (startup.sh),在该文件中加入启动参数:

CATALINA_OPTS="$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib"

(3)判断安装成功:

如果看到下面的启动日志,表示成功。

2007-4-26 15:34:32 org.apache.coyote.http11.Http11AprProtocol init



常见tomcat内存错误1:Java.lang.OutOfMemoryError: Java heap space

使用Java程序从数据库中查询大量的数据时出现异常

在JVM中如果98%的时间是用于GC(Garbage Collection)且可用的Heap size 不足2%的时候将抛出此异常信息。

整体意思是超出内存堆空间的错误

OutOfMemeoryError:超出内存的错误

space:空间的意思

heap:堆

堆是什么意思?

堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,所有方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。

JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。

要加“m”说明是MB,否则就是KB了,在启动tomcat时会 报内存不足。

-server表示以server模式运行,运行效率比默认的client高很多

-Xms:初始值
-Xmx:最大值
-Xmn:最小值

一般的JAVA程序在运行都可以通过中-Xms -Xmx来调整应用程序的初始内存和最大内存: 

  如:java -Xms64m   -Xmx128m   a.jar.

tomcat的启动程序是包装过的,不能直接使用java -Xms64m   -Xmx128m   tomcat.*来改变内存的设置,而是只要去改变catalina.sh文件的值就行。


常见tomcat内存错误2:java.lang.OutOfMemoryError: PermGen space

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。

问题的关键是SUN 的JVM把内存分了不同的区,其中一个就是permenter区用来存放用得非常多的类和类描述。本来SUN设计的时候认为这个区域在JVM启动的时候就固定了,但他没有想到现在动态映射会用得这么广泛。而且这个区域有特殊的垃圾收回机制,现在的问题是动态加载类到这个区域后,gc根本没办法回收!,现在只能通过加大内存来解决。

因为对于操作系统,请求内存的系统调用会占用大量的cpu时间,所以频繁的请求、释放内存将会导致性能的严重下降。所以对于jvm,最好的方式就是尽量多占用内存作为heap,少释放甚至不释放空闲的heap给操作系统以减少消耗在内存请求、释放操作上的cpu时间。  

设置NewSize、MaxNewSize相等,"new"的大小最好不要大于"old" 的一半,原因是old区如果不够大会频繁的触发主GC,大大降低了性能

JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64,由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4

常见tomcat内存错误3:OutOfMemoryError: unable to create new native thread.

无法创建新的线程。

每一个32位的进程最多可以使用2G的可用内存,因为另外2G被操作系统保留。这里假设使用1.5G给JVM,那么还余下500M可用内存。这500M内存中的一部分必须用于系统dll的加载,那么真正剩下的也许只有400M,现在关键的地方出现了:当你使用Java创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程(参考JVM规范),操作系统会在余下的 400兆内存里创建这个物理线程,而不是在JVM的1500M的内存堆里创建。在jdk1.4里头,默认的栈大小是256KB,但是在jdk1.5里头,默认的栈大小为1M每线程,因此,在余下400M的可用内存里边我们最多也只能创建400个可用线程。

这样结论就出来了,要想创建更多的线程,你必须减少分配给JVM的最大内存。还有一种做法是让JVM宿主在你的JNI代码里边。

给出一个有关能够创建线程的最大个数的估算公式:

(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

对于jdk1.5而言,假设操作系统保留120M内存:

1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads
1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads

因此:我们需要结合不同情况对tomcat内存分配进行不同的诊断才能从根本上解决问题。


常见tomcat内存错误3:java "Too small initial heap" 错误  

Error occurred during initialization of VM
Too small initial heap for new size specified
Error occurred during initialization of VM
Too small initial heap for new size specified

在网上google了下,说是Xmx设置小了。

将catalina.sh文件中的:

export JAVA_OPTS=-Xms128m -Xmx1024m

 改为:

set VM_ARGS=-Xms256m -Xmx1024m

程序运行正常。

原因分析:

启动应用时,通过使用-X选项来分配JVM的存储。

具体解决方案

centos 环境下面的操作

vi /opt/tomcat6/bin/catalina.sh

在文件头部的注释下面,加入下面的内容

#tomcat堆内存设置,假设内存空间为4G

export JAVA_OPTS='-server -Xms1024m -Xmx1024m -Xmn256m  -XX:PermSize=128m -XX:MaxNewSize=512m -XX:MaxPermSize=512m '

#让tomcat自己管理内存

export JAVA_OPTS=$JAVA_OPTS -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="$CATALINA_HOME\conf\logging.properties"

windows下面的tomcat

#tomcat堆内存设置,假设内存空间为4G

set JAVA_OPTS='-server -Xms1024m -Xmx1024m -Xmn256m  -XX:PermSize=128m -XX:MaxNewSize=512m -XX:MaxPermSize=512m '

#让tomcat自己管理内存

set  JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_HOME%\conf\logging.properties"


常见错误4:警告: Parameters: Invalid chunk ignored. 

访问网页的时候后台经常错误,今天在http://7sunet.iteye.com/blog/291247这个文章中有具体分析了一下原因

org.apache.tomcat.util.http.Parameters processParameters 

造成以上问题的有这么几种原因: 

1、访问/test.jsp?&p1=1&p2=2... 

2、访问/test.jsp?p1=1&p2=&p3=3... 

3、访问/test.jsp?p1=1&&p2=2... 

4、访问/test.jsp?action=save&.... 

5、表单提交时,存在<input name="" value="***" />这样的域 

总之,代码编写的不规范容易带来这样的问题。  

action是<form action="">的特殊标签,在有些服务器如Tomcat的某些版本中,会出现识别上的问题,所以应用时,应尽量不使用action作为request 参数

另外warn类型的日志,可以在服务器日志中找,Tomcat6好像在logs/catalina.log中

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://sulao.cn/post/390.html

我要评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。