5

Tomcat优化,值得你拥有!

 3 years ago
source link: https://leishen6.github.io/2020/08/24/tomcat_optimize/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的;应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能;除此之外也必然会提到对应的JVM参数的优化的一些经验。

本文为 转载文章 ,原文地址:系统优化怎么做-Tomcat优化

Tomcat的运行模式:

运行模式分3种模式:

  • bio:默认的模式,效率比较低
  • nio:优化时使用的模式
  • apr:对系统配置有一些比较高的要求

确认Tomcat运行模式:

查找配置文件 server.xml , 在tomcat下的路径:conf 目录下;

Executor 为自定义配置 Tomcat 线程池:

1
2
3
4
5
<Executor name="tomcatThreadPool" 
namePrefix="catalina-exec-"
maxThreads="1024"
minSpareThreads="512"
prestartminSpareThreads="true" />

关键配置:

maxThreads:

最大线程数,默认是200

minSpareThread:

最小活跃线程数,默认是25

maxQueueSize:

最大的等待队列个数,超过则请求拒绝默认值是Integer.MAX_VALUE ,一般不改变。在某些紧急状态修复问题需要调整

连接器(Connector):

Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。

nio 配置:

配置文件 server.xml

1
2
3
4
5
6
7
8
9
10
<!-- 运行模式为 nio -->
<Connector port="14081" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
<!-- 连接器中连接处理 使用上面自定义的 线程池中的线程 -->
executor="tomcatThreadPool"
URIEncoding="UTF-8"
compression="on"
useBodyEncodingForURI="true"
enableLookups="false"
redirectPort="14443" />

影响性能的配置:

protocol:

org.apache.coyote.http11.Http11Protocol - 阻塞式的Java连接器
org.apache.coyote.http11.Http11NioProtocol - 不阻塞Java连接器
org.apache.coyote.http11.Http11AprProtocol - APR / native 连接器
选择不阻塞Java连接器

enableLookups:

若是你想request.getRemoteHost()的调用履行,以便返回的长途客户端的实际主机名的DNS查询,则设置为true。设置为false时跳过DNS查找,并返回字符串的IP地址(从而提高性能)。 默认场景下,禁用DNS查找

compression:

设置成on,开启压缩

禁用AJP链接器:

使用Nginx+tomcat的架构,用不着AJP协议,所以把AJP连接器禁用
server.xml注释掉以下配置:

1
<Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />

优化 JVM:

优化位置:/bin/catalina.sh

修改 JAVA_OPTS 参数,这里需要参照 机器配置 ,对JVM进行参数优化 。

JDK1.7:
1
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"
JDK1.8:
1
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"

注意:1.8 中已经没有 永久代了,所以也就没有 没有PermSize、MaxPermSize ;Java8 中将永久代改为了 元空间 了,JAVA8里对metaspace可以在小范围自动扩展永生代避免溢出。

参数说明:

  • -Djava.awt.headless

没有设备、键盘或鼠标的模式。

  • -Dfile.encoding

设置字符集

  • -server

jvm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式

  • -Xms1024m

初始Heap大小,使用的最小内存

  • -Xmx1024m

Java heap最大值,使用的最大内存
经验: 设置Xms大小等于Xmx大小

  • -XX:NewSize=512m

表示新生代初始内存的大小,应该小于 -Xms的值

  • -XX:MaxNewSize=1024M

表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值

  • -XX:PermSize=1024m

设定内存的永久保存区域,内存的永久保存区域,VM 存放Class 和 Meta 信息,JVM在运行期间不会清除该区域; 一般情况下,此参数值使用默认即可,默认大小就够用了

程序加载很多class情况下,超出PermSize情况下:
JDK1.7会抛出java.lang.OutOfMemoryError: PermGen space异常
JDK1.8下会抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常

  • -XX:MaxPermSize=1024m

设定最大内存的永久保存区域
经验: 设置PermSize大小等于MaxPermSize大小

  • -XX:+DisableExplicitGC

自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作,避免程序员在代码里进行System.gc()这种危险操作。System.gc() 除非是到了万不得也的情况下使用,都交给JVM吧

其他参数优化:

  • X:SurvivorRatio=2

年轻代中Eden区与Survivor区的大小比值

  • -XX:ReservedCodeCacheSize=256m

保留代码占用的内存容量,无大的影响

  • -Xss1024k

单个线程堆栈大小值,减少这个值可以生成更多线程,操作系统对于一个进程内的线程数是有限制的,经验值在3000-5000左右

  • -XX:+CMSParallelRemarkEnabled

CMS 垃圾回收算法,对响应时间的重要性需求 大于 对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用

  • -XX:+UseCMSCompactAtFullCollection

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

  • -XX:+UseCMSInitiatingOccupancyOnly

在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。

  • -XX:CMSInitiatingOccupancyFraction=60

使用cms作为垃圾回收, 使用60%后开始CMS收集

  • -XX:+UseGCOverheadLimit

用来限制使用内存,如果不做控制,可能会报出
java.lang.OutOfMemoryError: GC overhead limit exceeded

  • -XX:+UseConcMarkSweepGC

使用CMS内存收集

  • -XX:+UseParNewGC

设置年轻代为并行收集

  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=/x/dump_tomcat.hprof

JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。

  • -Xloggc:/xx/gc_tomcat.log

gc的日志,如果该日志中出现频繁的Full GC就是有相关的系统问题,如果很少,说明暂时还算正常

  • -XX:+PrintGCDateStamps

输出GC的时间戳(以基准时间的形式)

  • -XX:+PrintGCDetails

输出GC的日志格式

  • -Dnetworkaddress.cache.ttl=60
  • -Dsun.net.inetaddr.ttl=60

设置DNS缓存时间

  • -DautoStartup=false
  • -Dsun.net.client.defaultConnectTimeout=60000

连接建立超时时间

  • -Dsun.net.client.defaultReadTimeout=60000

内容获取超时设置

  • -Djmagick.systemclassloader=no

是否生成缩略图的一个框架的配置

  • -Djava.security.egd=file:/dev/./urandom

最佳实践:

1
export JAVA_OPTS="-server -showversion -Xms2000m -Xmx2000m -Xmn500m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=2 -XX:ReservedCodeCacheSize=256m -Xss1024k -Djava.awt.headless=true -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat_path/logs/dump_tomcat.hprof -Xloggc:/tomcat_path/logs/gc_tomcat.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCDetails -Dnetworkaddress.cache.ttl=60 -Dsun.net.inetaddr.ttl=60 -DautoStartup=false -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"

常见 JVM 异常:

  1. java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出:

    JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。

    解决方法:

    • 首先检查代码,是否存在创建了大量无用对象,且其被引用着,无法被GC回收 的代码;
    • 手动设置 JVM Heap(堆)的大小;
  1. java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出:

    jdk1.8 抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常
    PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。

    解决方法: 手动设置 MaxPermSize 大小;

  1. java.lang.StackOverflowError —- 栈溢出:

​ 栈溢出了,JVM 依然是采用栈式的虚拟机。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。

解决方法: 代码中递归也不要递归的层次过多;

❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK