前言

关于 Tomcat 性能调优,一直以来就是运维面试的一个重要话题。今天我们就简单聊聊 Tomcat 如何进行性能优化? 首先声明,我不会去说 Tomcat 是什么,内部结构,原理什么的。我不懂......我只是会说一些我在工作当中的一些参数以及自己所了解的方法,主要还是和大家沟通、交流。

一、关于选型

简单说明,关于Openjdk 和 Oracle jdk的选择,我个人比较倾向于使用Oracle jdk,虽然它很流氓但是我觉得东西还是靠谱;其次是在版本上的选择,建议选择最新稳定版进行在生产环境使用,以此来获得更高效的性能;

一、JVM相关参数优化

export JAVA_OPTS=""-Dfile.encoding=UTF-8 -server -Xms1400M -Xmx1400M -Xss512k
-XX:+AggressiveOpts
-XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M
-XX:+DisableExplicitGC -XX:MaxTenuringThreshold=30 -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:ReservedCodeCacheSize=32m
-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly
-Djava.awt.headless=true"

提示:在java 8中永久代已被移除,如果你是java8的环境,需要你去掉"-XX:PermSize=128M -XX:MaxPermSize=256M"参数,并且"-XX:MaxTenuringThreshold=31"参数值只能设置在0-15,否则会提示相关错误;

配置说明:

  • Tomcat 默认是以 java -client 的方式运行,server 意味着是已真正的生产环境来运行,这样可以获得更高的并发、更高效的垃圾回收能力;
  • Xms、Xmx表示JVM 最小内存初始值和最大内存初始值,建议设置为相同参数,以减少CPU对内存资源的调度,避免CPU高速运转进行的垃圾回收;
  • 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()”。调用System.gc()付出的代价就是系统响应时间严重降低,就和我在关于Xms,Xmx里的解释的原理一样;
  • -XX:+UseParNewGC 对年轻代采用多线程并行回收。
  • -XX:+UseConcMarkSweepGC 即CMS gc,这一特性只有jdk1.5即后续版本才具有的功能,它使用的是gc估算触发和heap占用触发。我们知道频频繁的GC会造面JVM的大起大落从而影响到系统的效率,因此使用了CMS GC后可以在GC次数增多的情况下,每次GC的响应时间却很短,比如说使用了CMS GC后经过jprofiler的观察,GC被触发次数非常多,而每次GC耗时仅为几毫秒;
  • -XX:+CMSParallelRemarkEnabled在使用UseParNewGC 的情况下, 尽量减少mark的时间;
  • -XX:+UseCMSCompactAtFullCollection在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少;
  • -XX:LargePageSizeInBytes指定 Java heap的分页页面大小;
  • -XX:CMSInitiatingOccupancyFraction=70CMSInitiatingOccupancyFraction,这个参数设置有很大技巧,基本上满足(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关联在一起;
三、Tomcat 自身优化
3.1 修改Tomcat Connector运行模式

Tomcat Connector(Tomcat连接器)有bio、nio、apr三种运行模式,简单介绍(抄百度的,有本事来打我):

  • 1. bio(blocking I/O),即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包 及其子包)。Tomcat在默认情况下(敲黑板,主要是在tomcat7或以下的版本),就是以bio模式运行的。遗憾的是,就一般而言,bio模式是三种运行模式中性能最低的一种。

  特性:一个线程处理一个请求。缺点:并发量高时,线程数较多,浪费资源。

  • 2. nio(new I/O),是Java SE 1.4及后续版本提供的一种新的I/O操作方式(即java.nio包及其子包)。Java nio是一个基于缓冲区、并能提供非阻塞I/O操作的Java API,因此nio也被看成是non-blocking I/O的缩写。它拥有比传统I/O操作(bio)更好的并发运行性能。nio目前存在nio以及nio2模式,因此到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持。tomcat 8默认以 nio 启动,其他版本自己百度怎么修改为 nio,在启动的时候观察日志:
12-Dec-2017 14:17:46.429 信息 [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
12-Dec-2017 14:17:46.445 信息 [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
12-Dec-2017 14:17:46.473 信息 [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-nio-8009"]
12-Dec-2017 14:17:46.474 信息 [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
12-Dec-2017 14:17:46.476 信息 [main] org.apache.catalina.startup.Catalina.load Initialization processed in 1031 ms

特性:利用Java的异步IO处理,可以通过少量的线程处理大量的请求。

  • 3. apr(Apache Portable Runtime/Apache可移植运行时),是Apache HTTP服务器的支持库。在tomcat中,又被称为 tomcat native,从操作系统层面解决io阻塞问题。是一个利用 APR 来提升Tomcat性能的本地API。怎么配置?自己查!生产环境,强烈推荐。

3.2 tomcat server.xml配置优化

线程池参数配置:

<?xml version="1.0" encoding="UTF-8"?>
<Service name="Catalina"> <!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="1000"
minSpareThreads="100"
maxIdleTime="60000"
prestartminSpareThreads = "true"
maxQueueSize = "100"
className="org.apache.catalina.core.StandardThreadExecutor" />
  • URIEncoding="UTF-8" 使得tomcat可以解析含有中文名的文件的url;
  • minSpareThreads 最小备用线程数,tomcat启动时的初始化的线程数。
  • enableLookups 消除DNS查询对性能的影响我们可以关闭DNS查询。
  • connectionTimeout为网络连接超时时间毫秒数。
  • maxThreads Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数。建议值为500-1000,具体根据服务器性能以及压测数据结果测试;
  • acceptCount是当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection。
  • maxProcessors与minProcessors在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。

提示:网上很多混淆了,异步servlet和非阻塞connector,一个是Executor,一个是connector,两者的工作阶段不同。

连接器配置:

    <Connector executor="tomcatThreadPool" URIEncoding="utf-8"
port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
enableLookups="false"
maxConnections="2000"
useURIValidationHack="false"
keepAliveTimeout="60000"
connectionTimeout="20000"
maxThreads="1000"
minSpareThreads="100"
maxSpareThreads="2000"
minProcessors="100"
maxProcessors="1000"
tcpNoDelay="true"
redirectPort="8443" />
  • maxThreads="X" 表示最多同时处理X个连接;
  • minSpareThreads="X" 初始化X个连接;
  • maxSpareThreads="X" 表示如果最多可以有X个线程,一旦超过X个,则会关闭不在需要的线程;
  • acceptCount="X" 当同时连接的人数达到maxThreads时,还可以排队,队列大小为X.超过X就不处理;

提示:Tomcat 中可以同时接收的连接数为maxConnections+acceptCount 。上面这些参数需要手动开启,默认值配置都较低,无法发挥最佳性能。可以去你们生产环境看看,是不是很多没有做优化呢;

四、关于性能分析

4.1 常见工具

  • 1 jmx 端口获取监控信息(比如,zabbix);
  • 2 jps/jstat/jmap等命令进行调试;
  • 3 java agent方式获取以及代码植入以探针的方式监控(开源PinPoint);

 4.2 其他

查看tomcat连接数:

netstat –nat | grep 8080

查看 tomcat 线程数:  

# 查看进程ID
ps -ef |grep java # 查看线程
ps -o nlwp 3598

提示:通过使用该方式可以查看到该进程有多少线程,但并没有排除处于idle状态的线程。所以,要想获取当前进程running的线程数,还需要执行如下命令。

ps -eLo pid,stat | grep 3598 | grep running | wc -l

其中ps -eLo pid,stat可以找出所有线程,并打印其所在的进程号和线程当前的状态;两个grep命令分别筛选进程号和线程状态;wc统计个数。SL表示空闲状态。 

关于tomcat性能优化的更多相关文章

  1. tomcat 性能优化

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

  2. Tomcat性能优化(转载)

    出处:微信订阅号GitChat精品课程 — Tomcat性能优化 Tomcat 简单介绍 Sun 公司创建了第一个 Servlet 容器,即 Java Web Server,但 JWS 只是为了演示 ...

  3. tomcat 性能优化(内存优化 线程优化)

    转自:http://blog.sina.com.cn/s/blog_4b5bc01101014s81.html tomcat 性能优化 linux修改TOMCAT_HOME/bin/catalina. ...

  4. Tomcat性能优化之(一) 启动GZIP压缩

    Tomcat性能优化之(一) 启动GZIP压缩 1:设置TOMCAT启用GZIP压缩,通过浏览器HTTP访问对应的资源会根据配置进行压缩. <Connector port="8080& ...

  5. JVM虚拟机详解+Tomcat性能优化

    1.JVM(java virtual mechinal) ()JVM有完善的硬件架构,如处理器.堆栈.寄存器当,还具有相应的指令系统. ()JVM的主要工作时解释自己的指令集(即字节码),并映射到本地 ...

  6. tomcat性能优化梳理

    tomcat性能优化 Tomcat本身优化 Tomcat内存优化 启动时告诉JVM我要一块大内存(调优内存是最直接的方式) 我们可以在 tomcat 的启动脚本 catalina.sh 中设置 jav ...

  7. 闲谈Tomcat性能优化

    Tomcat在各位JavaWeb从业者常常就是默认的开发环境,但是Tomcat的默认配置作为生产环境,尤其是内存和线程的配置,默认都很低,容易成为性能瓶颈. 幸好Tomcat还有很多的提升空间.下文介 ...

  8. tomcat 性能优化(转)

    tomcat nginx默许的post大小限制 tomcat nginx默认的post大小限制执行大文件上传,或者,大数据量提交时,当提交的数据大小超过一定限制时,发现后台从request取值的代码r ...

  9. Tomcat性能优化及JVM内存工作原理

    Java性能优化原则:代码运算性能.内存回收.应用配置(影响Java程序主要原因是垃圾回收,下面会重点介绍这方面) 代码层优化:避免过多循环嵌套.调用和复杂逻辑. Tomcat调优主要内容如下: 1. ...

  10. Tomcat性能优化及常用命令整理

    1汤姆猫性能优化 1.1连接参数 1.1.1默认连接配置 默认连接器采用阻塞式 IO,默认最大线程数为200,配置如下: <Connector port="8080" pro ...

随机推荐

  1. nmap学习之相关参数列表

    一.TARGET SPECIFICATION: Can pass hostnames, IP addresses, networks, etc. Ex: scanme.nmap.org, micros ...

  2. u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

    一.重定位 1.以前版本的重定位 2.新版本 我们的程序不只涉及一个变量和函数,我们若想访问程序里面的地址,则必须使用SDRAM处的新地址,即我们的程序里面的变量和函数必须修改地址.我们要修改地址,则 ...

  3. [C++]指针与引用(应用辨析)

    1.指针变量允许将一个整数经强制转换后赋值给指针变量    Eg:      float *fp;      fp = (float *)5000;//意义:将5000作为一个地址赋给指针变量fp 2 ...

  4. luogu P4289 [HAOI2008]移动玩具

    传送门 这道题可以二进制记录状态搜索 也可以做以下考虑 若一个棋子要移动到另一个位置上去,则步数为两点的曼哈顿距离(横坐标差的绝对值+纵坐标差的绝对值),因为假设路径上有其他的棋子,可以通过移动其他棋 ...

  5. 第16月第15天 glut

    1. https://tokoik.github.io/opengl/libglut.html https://github.com/wistaria/wxtest/tree/master/C htt ...

  6. mysql timestamp时区有影响

    timestamp格式 如果数据库修改时区,可能会对数据影响,datetime不依赖数据库时区

  7. JSON的理解

    官方解释: JSON的全称是”JavaScript Object Notation”,单单从字面上的理解就是JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式. 理解: ...

  8. Linux时间日期类指令

    ⒈date [Options] 显示/设置当前日期 基本语法: date 显示当前时间 date +"%Y" 显示当前年份 date +"%m" 显示当前月份 ...

  9. 【windows核心编程】远程线程DLL注入

    15.1 DLL注入 目前公开的DLL注入技巧共有以下几种: 1.注入表注入 2.ComRes注入 3.APC注入 4.消息钩子注入 5.远线程注入 6.依赖可信进程注入 7.劫持进程创建注入 8.输 ...

  10. Python中的__init__()和__call__()函数

    Python中的__init__()和__call__()函数 在Python的class中有一些函数往往具有特殊的意义.__init__()和__call__()就是class很有用的两类特殊的函数 ...