jdk8和Tomcat8.5是JDK和Tomcat的史诗级提升,直接把单车变跑车,所以如果你还是使用的jdk7和Tomcat8.5以后的版本,那可以考虑去线上换一下,但是不知道到时是架构师打死你还是你打死架构师

Tomcat的四种运行模式:

  • BIO:同步阻塞模式,Tomcat7及之前默认的模式,性能最差,Tomcat8.5后已抛弃
  • NIO:同步非阻塞模式,Tomcat8及之后默认的模式,性能高,现在的主流模式
  • AIO:异步非阻塞模式,由于纯异步,性能最高
  • APR:需要额外安装依赖库,从操作系统级别解决异步IO,大幅度的提高服务器的处理和响应性能,也是Tomcat运行高并发应用的首选模式,但是使用困难,依赖系统的底层网络包,团队没有C++大神和熟悉Linux底层的大神还是别拿出来秀了

主流是NIO模式,我们主要学习NIO模式的

配置优化

server status

通过配置可以看到Tomcat管理页server status,利用server status帮我们获取Tomcat的信息
我们需要修改一下配置文件,conf/tomcat-users.xml和
webapps/manager/META-INF/context.xml。
找到conf/tomcat-users.xml

<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-status"/>
<user username="tzb" password="123456" roles="manager-gui,admin-gui,manager-script,manager-jmx,manager-status"/>

找到
webapps/manager/META-INF/context.xml,将以下内容注释掉,我已经注释掉了,这样就能远程访问web manager了:

image.png

image.png

通过server status可以看到jvm的信息,还要很多的选项可供,例如应用程序列表、JVM信息、NIO模型等

image.png

禁用ajp协议

ajp协议是基于TCP协议的,Tomct使用ajp协议主要是为了连接http apache服务器的,http apache这个服务器已经被Ngnix完爆了,如果没有什么特殊癖好的话应该没人会用它了,但是这个配置会在程序中默认跑着十个线程,所以为了性能把这个没用的功能去除,如果不是2020年2月以后下载的Tomcat的话,是没有默认禁用的,所以需要手动禁掉

我的Tomcat是默认禁止的,我在配置中开启了ajp,我们看到这里跑了十个线程,跑了线程又用不到,浪费CUP的性能

image.png

在conf/server.xml中把这个配置注释了就禁止掉ajp了(如果你有特殊的癖好,想打开ajp,就把注释打开, 并且把secretRequired="true"修改为secretRequired="")

image.png

重启服务器,查看server status,ajp已经没有了

image.png

自定义Tomcat线程池

Tomcat需要给每一个请求创建线程,Tomcat默认的线程池最大线程数是200,核心线程数是10,如何并发高的场景,Tomcat就得不断的创建和销毁线程,所以就得自定义线程池提高核心线程数,这样可以帮助我们提高性能。但是如果不是是连接请求特别多的场景,最后别乱改,核心线程是需要占用内存的

先看看我们没修改前的状态,我使用Jvisualvm工具监控Tomcat的线程状态,我们看到这里默认是十个线程,由于我用的是Tomcat8.5所以运行模式默认是nio模式,线程的前缀是exec,记住这个名字后面用到的

image.png

修改server.xml文件,打开Executor的注释,我配置的参数是:最大线程数150,核心线程数15,线程池名称,每个线程的前缀。
为了区别,我把线程的前缀改为tzb-nb-,接着把默认的连接器配置注释掉,打开下面的连接器,让自定义的连接线程池生效

image.png

image.png

重启服务器,查看Jvisualvm,我们看到连接池已经生效了,整好十五个

image.png

Tomcat线程模型

我们主要讲Tomcat8后的NIO,因为NIO才是主流,Tomcat的线程有很多,主线程叫做main是负责启动和关闭

Tomcat的主要线程

看监控工具可以看到Tomcat的主要线程

Tomcat的主要线程

  • Acceptor

使用NIO的原生ServerSocketChannel的accept()方法监听客户的连接请求,但是这个ServerSocketChannel是设置阻塞的,所以当没有连接请求来时,线程会阻塞在accept方法上。ServerSocketChannel设置的TCP连接队列大小默认是100,TCP连接队列就是把当前连接信息存放到全连接队列中,队列中的连接信息等待ServerSocket.accpt()处理,Acceptor队列由acceptCount控制,但是Tomcat保持的通道数默认是10000,也就是ServerSocket.accpt()进去的通道Tomcat能保持10000个,由maxConnections控制。然后将ServerSocket.accpt()监听获取到的客户端通道SocketChanel设置为非阻塞,同时将SocketChanel和注册事件封装成一个PollerEvent对象扔进队列SynchronizedStack中,SynchronizedStack内部是数组,然后队列里的PollerEvent等待Poller线程来注册处理,Poller线程内部就是使用了NIO中的Selector多路复用器,PollerEvent对象主要是装载了SocketChanel和注册事件OP_REGISTER在Poller内部其实就是给Selector注册OP_READ,PollerEvent其实本身也是个线程。Poller线程是队列的消费者,Acceptor是生产者。这个Acceptor线程默认只有1个,但是可以在Tomcat中配置数量

 public void bind() throws Exception {if (!getUseInheritedChannel()) {
//Acceptor的ServerSocketChannelserverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = (getAddress()!=null?
new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));serverSock.socket().bind(addr,getAcceptCount());//getAcceptCount()默认是100} else {//............................省略
//设置为阻塞的连接监听通道serverSock.configureBlocking(true);//............................省略
    protected final void startAcceptorThreads() {
//Acceptor的线程个数,这个是可以通过配置文件配置的int count = getAcceptorThreadCount();
//创建Acceptor线程acceptors = new Acceptor[count];for (int i = 0; i < count; i++) {acceptors[i] = createAcceptor();String threadName = getName() + "-Acceptor-" + i;acceptors[i].setThreadName(threadName);Thread t = new Thread(acceptors[i], threadName);t.setPriority(getAcceptorThreadPriority());t.setDaemon(getDaemon());
//启动Acceptor线程t.start();}}
 protected class Acceptor extends AbstractEndpoint.Acceptor {//............................省略@Overridepublic void run() {//............................省略while (running) {
//计数+1,达到最大值则等待,tomcat设定个最大连接数是10000,达到这个阈值后,就会拒绝连接请求,进行阻塞,这个是用AQS阻塞队列实现的countUpOrAwaitConnection();SocketChannel socket = null;try {
//ServerSocketChannel开始监听连接socket = serverSock.accept();//............................省略if (running && !paused) { if (!setSocketOptions(socket)) {//setSocketOptions设置socket的方法closeSocket(socket);}//............................省略private void closeSocket(SocketChannel socket) {
//断开连接计数器将会减1countDownConnection();
 protected boolean setSocketOptions(SocketChannel socket) {try {//将监听到的SocketChannel 设置为非阻塞socket.configureBlocking(false);Socket sock = socket.socket();//............................省略
//注册到ClientPoller的方法getPoller0().register(channel);//............................省略}
   public void register(final NioChannel socket) {//............................省略if ( r==null)//封装成PollerEvent,与OP_REGISTER注册事件绑定r = new PollerEvent(socket,ka,OP_REGISTER);else r.reset(socket,ka,OP_REGISTER);
//添加到队列addEvent(r);}
    public static class PollerEvent implements Runnable {//............................省略@Overridepublic void run() {
//注册事件,我们看到注册事件其实就是OP_READ读事件if (interestOps == OP_REGISTER) {try {
//将事件和通道注册到ClientPoller中的多路复用器中socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
  • ClientPoller

Tomcat8以后的NIO模式比Tomcat7以前强悍就是因为这个ClientPoller,ClientPoller是实现了Runnable,ClientPoller线程内部有一个jdk原生的Selector对象,也就是NIO的多路复用器。ClientPoller线程从队列SynchronizedQueue中取出PollerEvent逐一启动,启动后PollerEvent将SocketChanel和对应的事件逐一注册注册到ClientPoller的Selector,Selector找出SocketChanel中就绪的SelectionKey,将SelectionKey和事件类型交给工作线程Exec进行处理。整个过程就是典型的NIO实现。Tomcat默认启动Math.min(2,Runtime.getRuntime().availableProcessors())个ClientPoller线程,绝大部分的情况是2个ClientPoller,也就是两个Selector需要处理成千上万的事件,非常繁忙

Poller的run方法部分代码

Poller的run方法部分代码

//processKey源码大致逻辑,省略详细代码protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {//............................省略if ( close ) {//............................省略} else if ( sk.isValid() && attachment != null ) {if (sk.isReadable() || sk.isWritable() ) {// 写事件if ( attachment.getSendfileData() != null ) {processSendfile(sk,attachment, false);} else {if (sk.isReadable()) {//读事件if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {closeSocket = true;}}if (!closeSocket && sk.isWritable()) {// 写事件if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {closeSocket = true;}}if (closeSocket) {............................// 关闭通道}..................................
}
  • NioBlockingSelector.BlockPoller

NioBlockingSelector主要处理socketChanel的写操作,也就是servlet的回写过程,NioBlockingSelector有两个对象BlockPoller和Selector叫做sharedSelector。BlockPoller中也有一个Selector对象,这个Selector主要是在blockingSelector.write时如果出现写失败就把socketChanel注册到BlockPoller的Selector不在占用ClientPoller的时间片,然后就利用CountDownLatch进行阻塞这是Tomcat默认的写操作过程,在BlockPoller的Selector注册的socketChanel以后的读写操作都是由这个Selector阻塞轮询。同时NioBlockingSelector是NioSelectorPool中一个成员对象,NioSelectorPool中有两个成员对象NioBlockingSelector和一个Selector叫做SHARED_SELECTOR,SHARED_SELECTOR主要在非默认的写过程中处理由ClientPoller轮询的socketChanel出现写失败时,为了节省ClientPoller宝贵的时间片socketChanel写事件会注册到SHARED_SELECTOR上,这个SHARED_SELECTOR是叫做辅Selector,值得一提的是NioSelectorPool的SHARED_SELECTOR、NioBlockingSelector的sharedSelector、BlockPoller的Selector都是同一对象,这个辅Selector轮询出现写事件失败的socketChanel,由辅Selector负责这些socketChanel以后的读写事件,这样减少线程间的切换,同时还可减轻主Selector的负担。

public class NioSelectorPool {
//SHARED 默认是trueprotected static final boolean SHARED =Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.net.NioSelectorShared", "true"));protected NioBlockingSelector blockingSelector;protected volatile Selector SHARED_SELECTOR;//........................省略public int write(ByteBuffer buf, NioChannel socket, Selector selector,long writeTimeout, boolean block) throws IOException {if ( SHARED && block ) {//block 和SHARED 是默认true,所以写事件默认走这里//阻塞写操作return blockingSelector.write(buf,socket,writeTimeout);}//........................省略if ( keycount > 0 ) { //only write if we were registered for a write
//写操做,cnt -1为失败cnt = socket.write(buf); //write the dataif (cnt > 0) {                 continue; }
//非阻塞写if (cnt==0 && (!block)) break; //don't block}if ( selector != null ) {//非阻塞写失败后重新注册到SHARED_SELECTOR,不占用ClientPoller的时间片if (key==null) key = socket.getIOChannel().register(selector, SelectionKey.OP_WRITE);else key.interestOps(SelectionKey.OP_WRITE);} else if (writeTimeout<0) {//SHARED_SELECTOR轮询keycount = selector.select();} else {keycount = selector.select(writeTimeout);}}
//.....................................................................省略
ublic class NioBlockingSelector {protected Selector sharedSelector;protected BlockPoller poller;
//阻塞写public int write(ByteBuffer buf, NioChannel socket, long writeTimeout)throws IOException {SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());try {while ( (!timedout) && buf.hasRemaining()) {if (keycount > 0) { //only write if we were registered for a writeint cnt = socket.write(buf); //write the datawritten += cnt;
//写操做,cnt -1为失败if (cnt > 0) {time = System.currentTimeMillis(); //reset our timeout timercontinue; //we successfully wrote, try again without a selector}}try {if ( att.getWriteLatch()==null || att.getWriteLatch().getCount()==0) att.startWriteLatch(1);
//失败则重新注册写事件到BlockPoller 中poller.add(att,SelectionKey.OP_WRITE,reference);
//利用CountDownLatch进行阻塞if (writeTimeout < 0) {att.awaitWriteLatch(Long.MAX_VALUE,TimeUnit.MILLISECONDS);} else {att.awaitWriteLatch(writeTimeout,TimeUnit.MILLISECONDS);}}
  • Exec

Exec是一个线程池很多博客叫它为Work,默认的线程名称是和server.xml中的一致,核心线程默认是10,最大线程数是200,可以通过我们上面的方法配置server.xml文件设置线程的数量,它主要是获取ClientPoller轮询出来的socketChanel后,做相应的逻辑处理:先进行三次握手,然后调用Http11Processor的service方法解析HTTP协议,将http协议解析成键值对放入request,最终调用CoyoteAdapter的service方法将request和response传入Tomcat的各种容器中执行servlet的业务逻辑。

image.png

//工作线程执行run处理socketChanelpublic boolean processSocket(SocketWrapperBase<S> socketWrapper,SocketEvent event, boolean dispatch) {try {if (socketWrapper == null) {return false;}//将Socket封装到这个对象,然后扔给工作线程执行SocketProcessorBase<S> sc = processorCache.pop();if (sc == null) {sc = createSocketProcessor(socketWrapper, event);} else {sc.reset(socketWrapper, event);}//工作线程,我们在配置文件中配置的Executor,不配默认是10个。最大数200Executor executor = getExecutor();//工作线程执行工作if (dispatch && executor != null) {executor.execute(sc);} else {sc.run();}
//工作线程执行的东西,主要是三次握手后开始解析HTTP,然后关闭socket,省略部分代码protected class SocketProcessor extends SocketProcessorBase<NioChannel> {public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {super(socketWrapper, event);}@Overrideprotected void doRun() {
if (handshake == 0) {//三次握手已完成SocketState state = SocketState.OPEN;// Process the request from this socketif (event == null) {state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);} else {state = getHandler().process(socketWrapper, event);}if (state == SocketState.CLOSED) {close(socket, key);}
..........................
//逻辑处理主要是调用了这给ConnectionHandler处理socket的数据包protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
//省略  ...............................................
//处理socket数据包,将HTTP其解析成request 对象,传给Tomcat容器@Overridepublic SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
//省略  ...............................................            do {
//省略  ...............................................            state = processor.process(wrapper, status);//真正继续数据包的方法,这个processor是AbstractProcessorLight 类型的,但是process方法主要是调用子类Http11Processor类实现的service进行处理
//省略  ...............................................            }}
}

点进process方法,里面调用了service

public abstract class AbstractProcessorLight implements Processor {@Overridepublic SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
//省略  ...............................................        throws IOException {} else if (status == SocketEvent.OPEN_READ) {
//这里是调用Http11Processor 的service方法state = service(socketWrapper);}
//省略  ...............................................        

Http11Processor 的service方法进行系列的解析各种

public class Http11Processor extends AbstractProcessor {
//省略  ............................................... @Overridepublic SocketState service(SocketWrapperBase<?> socketWrapper)throws IOException {
//省略  ...............................................
//经过一系列的解析和设置后,将http的各种信息都存放到request中调用CoyoteAdapter的service方法将request, response传递到Tomcat容器
getAdapter().service(request, response);
//省略  ............................................... }
}

Tomcat线程模型组件类的结构图

Tomcat线程模型动态图

NioEndpoint组件

NioEndpoint是Tomcat的NIO关键的类,要理解tomcat的nio最主要就是对NioEndpoint的理解

image.png

有空得自己去看看《Tomcat的内核设计剖析》

image.png

NioEndpoint它一共包含LimitLatch、Acceptor、Poller、SocketProcessor、Excutor5个部分。LimitLatch是连接控制器,它负责维护连接数的计算,nio模式下默认是10000,达到这个阈值后,就会拒绝连接请求。Acceptor负责接收连接,默认是1个线程来执行,将请求的事件注册到事件列表。有Poller来负责轮询,Poller线程数量是cpu的核数Math.min(2,Runtime.getRuntime().availableProcessors())。由Poller将就绪的事件生成SocketProcessor同时交给Excutor去执行。Excutor线程池的大小就是我们在Connector节点配置的maxThreads的值。在Excutor的线程中,会完成从socket中读取http request,解析成HttpServletRequest对象,分派到相应的servlet并完成逻辑,然后将response通过socket发回client。在从socket中读数据和往socket中写数据的过程,并没有像典型的非阻塞的NIO的那样,注册OP_READ或OP_WRITE事件到主Selector,而是直接通过socket完成读写,这时是阻塞完成的,但是在timeout控制上,使用了NIO的Selector机制,但是这个Selector并不是Poller线程维护的主Selector,而是BlockPoller线程中维护的Selector,称之为辅Selector

NioEndpoint Nio的时序图

Tomcat文件传输

sendfile零拷贝:

在普通的输入输出流进行读写时,实际上是进行了多次上下文切换,应用读取数据时,先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存,在用户态对数据进行加工,然后再从用户态切换回内核态,将用户缓存数据拷贝到内核缓存中,然后读到socket中再到网卡

sendfile实质是linux系统中一项优化技术,用以发送文件和网络通信时,减少用户态空间与磁盘倒换数据,而直接在内核级做数据拷贝,在Tomcat中是可以使用sendfile对一些静态数据如:图片、文件等进行传输,这个sendfile是在Tomcat是默认打开的,可以有效的提高Tomcat的传输性能

零拷贝

compression文件压缩:

compression可以对传输文件进行压缩有效解决文件传输的带宽问题,在Tomcat不支持图片压缩,因为要进行上层的用户态数据加工所以与sendfile互斥,Tomcat默认关闭,开启的话Tomcat会调用Filter利用jdk解压缩流进行解压缩

//源码中compression支持的压缩格式中没有图片private String compressibleMimeType = "text/html,text/xml,text/plain,text/css," +"text/javascript,application/javascript,application/json,application/xml";

Accept-Encoding 和Content-Encoding

Accept-Encoding 和Content-Encoding是HTTP中用来对采用哪种编码格式传输正文进行协定的一对头部字段。浏览器发送请求时,会在Request-heads携带Accept-Encoding会说明自己支持的编码列表,服务端在接收到请求后,从中挑选出一种用来对响应信息进行编码,并通过Response-heads携带Content-Encoding来说明服务端选择的编码信息,浏览器在拿到响应正文后,依据Content-Encoding进行解压。绝大部分是gzip格式

Tomcat重要配置参数

server.xml

自定义线程池我们上面讲过了,现在有了一定的基础更好介绍了

Executor标签是专门配置Exec线程池的,专门用于处理多路复用器传递进来的socketChanel,我们上面介绍过了

<Executor name="tomcatThreadPool"   namePrefix="tzb-nb-"   maxThreads="1000"   minSpareThreads="30"  maxIdleTime="60000"  maxQueueSize="1000000"  className="org.apache.catalina.core.StandardThreadExecutor"/>
        name:线程池名称,用于 Connector中指定。namePrefix:所创建的每个线程的名称前缀。maxThreads:池中最大线程数。minSpareThreads:核心池线程数。maxIdleTime:线程空闲时间,超过该时间后,非核心线程会被销毁,默认值为6000(1分钟),单位毫秒。maxQueueSize:在被执行前最大线程排队数目,任务数超出会执行拒绝策略,默认为Int的最大值className:线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。如果想使用自定义线程池首先需要实现 org.apache.catalina.Executor接口。

Connector用于配置Tomcat运行模式、Acceptor、CilentPoller、sendfile、是否开启文件压缩

<Connector port="8080"   protocol="HTTP/1.1"   maxThreads="1000"   minSpareThreads="100"  acceptorThreadCount="2"acceptCount="1000"  maxConnections="1000"  connectionTimeout="20000"   maxHttpHeaderSize="8192"  compression="on"  compressionMinSize="2048"  redirectPort="8443"  URIEncoding="UTF-8" />

我只列出几个有意思的

port:代表Tomcat监听端口,也就是网站的访问端口,默认为8080,可以根据需要改成其他。protocol:运行模式,可选类型有四种,分别为BIO,NIO,AIO和APR。
protocol="org.apache.coyote.http11.Http11NioProtocol"  //NIO
protocol="org.apache.coyote.http11.Http11Nio2Protocol" //AIO
protocol="org.apache.coyote.http11.Http11AprProtocol"  //ARPacceptCount:就是Acceptor线程工作时通道的长度也就是前面说的SynchronizedStack,当请求PollerEvent超出队列时请求就会被拒绝,默认是100。
一般是设置的跟 maxThreads一样或一半,此值设置的过大会导致排队的请求超时而未被处理。所以这个值应该是主要根据应用的访问峰值与平均值来权衡配置。acceptorThreadCount:Acceptor线程的数量,默认是1,如果在多核CPU架构下,此值可以设置为2,官方不建议设定超过2个的值。maxConnections:tomcat最大连接数也就是上面说的LimitLatch。NIO默认是10000,超出则Acceptor线程就被阻塞了,即不再队列中获取已经建立的连接。
但是它并不阻止新的连接的建立,新的连接的建立过程不是Acceptor控制的,Acceptor仅仅是从队列中获取新建立的连接。
所以当连接数已经超过maxConnections后,仍然是可以建立新的连接的,存放在上述acceptCount大小的队列中,这个队列里面的连接没有被Acceptor获取,
就处于连接建立了但是不被处理的状态。当连接数低于maxConnections之后,Acceptor线程就不再阻塞connectionTimeout:当请求已经被接受,但未被处理,也就是等待中的超时时间。单位为毫秒,默认值为60000。executor:就是把executor标签的内容引过来,不要它的话可以把executor的内容复制到这里URIEncoding:URL编码字符集。pollerThreadCount:ClientPoller的线程数,默认为2。官方不建议设置大于2的值,因为锁的竞争会导致性能下降,事实上一个线程也足够快速。useSendfile:是否开启sendfile特性,默认为true。对于web应用而言,通常project中还会包含一定数量的静态资源,比如图片、CSS、js、html等,sendfile在一定程度上可以提高性能。compression:是否开启压缩,这个属性与useSendfile互斥,useSendfile开启了,这个认关闭,compression是默认关闭的compressionMinSize:如果compression="on",则启用此项。被压缩前数据的最小值,也就是超过这个值后才被压缩。如果没有指定,这个属性默认为“2048”(2K),单位为byte。

实战:

image.png

我们看到Acceptor线程线程是3个、Client线程是5个,exec线程是15个和我上图的配置一致

image.png

catalina.sh

使用JAVA_OPTS配置JVM参数

//例如:
JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m
-XX:NewSize=512m -XX:MaxNewSize=1024m
-XX:PermSize=256m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"
  • 一般说来,您应该使用物理内存的 80% 作为堆大小。说明:以上两个参数关系到tomcat承受的访问性能,但也要根据服务器实际内存情况设定。有人建议Xms和Xmx的值取成一样比较好,说是可以加快内存回收速度。
  • Xms和Xmx这两个值的大小一般根据需要进行配置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得很重要,假如虚拟机启动时配置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为相同大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值配置为可用内存的最大值的80%。
  • 另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾能够接受的速度和应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。假如堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。假如您把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为确保最好的性能,要把堆的大小设大,确保垃圾收集不在整个基准测试的过程中出现。
  • 假如系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。假如垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的周详输出,研究 垃圾收集参数对性能的影响。一般说来,您应该使用物理内存的 80% 作为堆大小。当增加处理器时,记得增加内存,因为分配能够并行进行,而垃圾收集不是并行的。

Tomcat压力测试

使用jmeter可以进行对程序进行压力测试,根据用户的并发,调整Tomcat的线程数量

image.png

我使用一千个线程,一次并发一千,循环十次

image.png

主要看吞吐量,根据吞吐量慢慢调试Tomcat的线程设置

image.png

学习更多JAVA知识与技巧,关注与私信博主(学习)

后端调优基础——Tomcat调优相关推荐

  1. 后端调优基础——GC调优

    GC类型 Minor GC :清理新生代,Minor GC是最频繁触发的GC,速度也最快的,主要工作原理是: 对象在young区的eden创建,当eden空间满后触发Minor GC,将还存活的对象复 ...

  2. Tomcat 调优教程

    点击上方 IT牧场 ,选择 置顶或者星标技术干货每日送达! TIPS 本文基于Tomcat 9.0编写,理论兼容Tomcat 8.x及更高版本. 本文来探讨Tomcat的调优. Tomcat调优参数 ...

  3. zz温故知新:Tomcat调优JVM内存性能调优

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

  4. jvm与Tomcat调优【详解】——有这一篇就够了

    jvm与Tomcat调优 一.JVM性能调优 1.1 什么是JVM? 1.2 JVM调优工具 1.3 JVM调优经验 1.4常用JVM参数参考: 1.5 Java文件编译的过程 1.6 为什么说jav ...

  5. tomcat调优方案

    前段时间参加面试,面试过程中提到服务器的调优方案,这里总结一下. 首先说一下tomcat的调优方案: Tomcat本身的优化,Java虚拟机调优,Tomcat 优化分为系统优化,接下来一个个介绍: 一 ...

  6. Tomcat调优指南

    1          概述 本文档主要介绍了Tomcat的性能调优的原理和方法.可作为公司技术人员为客户Tomcat系统调优的技术指南,也可以提供给客户的技术人员作为他们性能调优的指导手册. 2    ...

  7. Tomcat调优技巧,实用

    人工智能,零基础入门!http://www.captainbed.net/inner 一.描述 最近,在学习接触到一些项目开发中的调优过程,刚好整理到 Tomcat 调优这块,有时候面试也会问到问,于 ...

  8. Java 9 中的 GC 调优基础

    转载自   Java 9 中的 GC 调优基础 在经过了几次跳票之后,Java 9终于在原计划日期的整整一年之后发布了正式版.Java 9引入了很多新的特性,除了闪瞎眼的Module System和R ...

  9. tomcat优化(五)tomcat调优

    Tomcat 调优大致分为两大类: 1.Tomcat 的自身调优: 采用动静分离节约 Tomcat 的性能 禁用 AJP 连接器 调整 Tomcat 的线程池 调整 Tomcat 的连接器 修改 To ...

最新文章

  1. 表单的几个基本常用功能
  2. django html5 video,Django Web中的静态文件之HTML5第1篇
  3. RabbitMQ —— 延迟队列
  4. Vue——vue-chartjs[Vue 对于 Chart.js 的封装]
  5. 耗尽您CPU资源的Explored病毒清除法
  6. 第一百零五期:5年前,跳槽涨薪,你笑了,5年后,跳槽降薪,你慌了!
  7. Linux ${}表达式详解
  8. 【图像风格转换】项目参考资料总结
  9. 最强编程大法,太强了!
  10. oracle fm s,FM连接oracle出错的问题??
  11. Java ConcurrentHashMap 最佳实践
  12. full outer join 与full join的区别_基础小白的SQL的JOIN语法解析
  13. 关于GPS数据跳动问题的解决方案
  14. C#实现发送短信功能
  15. matlab怎么表示对角阵,matlab – 如何基于每一行将矩阵转换为一堆对角矩阵?
  16. web前端期末大作业 html+css+javascript防天天生鲜官网网页设计实例 企业网站制作
  17. 【管理经验】管理的本质-激发善意
  18. python列表求平均数_python 列表平均值
  19. 工作流Airflow的性能优化,应对dag数目的激增
  20. Hello-Rust

热门文章

  1. c语言的翻译叫什么_C语言如何把它翻译成中文
  2. 30. Spark SQL case when用法:
  3. 国有企业数字化转型按下加速键,数据中台如何有效发力?| 数澜科技
  4. xutils3的应用
  5. Python中自定义函数参数类型
  6. 析构函数的作用和用法
  7. 常见的10种排序算法
  8. Java8的@sun.misc.Contended注解
  9. 无备份情况下恢复MySQL误删的表,这样做再也不用怕误删了
  10. ueditor编辑器自定义添加切换背景信纸功能JS操作