禁用编译优化_Tomcat8史上最全优化实践

news/2024/7/3 5:41:18

Tomcat8优化

    • 1、Tomcat8优化
      • 1.1、Tomcat配置优化
        • 1.1.1、部署安装tomcat8
        • 1.1.2 禁用AJP连接
        • 1.1.3、执行器(线程池)
        • 1.1.4 3种运行模式
      • 1.3、使用Apache JMeter进行测试
      • 1.3.1、下载安装
      • 1.3.2、修改主题和语言
      • 1.3.3、创建首页的测试用例
      • 1.3.4、启动、进行测试
      • 1.3.5、聚合报告
      • 1.4、调整tomcat参数进行优化
      • 1.4.1、禁用AJP服务
      • 1.4.2、设置线程池
      • 1.4.2.1、最大线程数为500,初始为50
      • 1.4.2.2、最大线程数为1000,初始为200
      • 1.4.2.3、最大线程数为5000,初始为1000
      • 1.4.2.4、设置最大等待队列数
      • 1.4.3、设置nio2的运行模式
      • 1.5、调整JVM参数进行优化
      • 1.5.1、设置并行垃圾回收器
      • 1.5.2、查看GC日志
      • 1.5.3、调整年轻代大小
      • 1.5.4、设置G1垃圾回收器
      • 1.5.5、小结
    • 2、JVM字节码
      • 2.1、通过javap命令查看class文件的字节码内容
      • 2.2、常量池
      • 2.3、描述符
      • 2.3.1、字段描述符
      • 2.3.2、方法描述符
      • 2.4、解读方法字节码
      • 2.4.1、图解
      • 2.5、研究i++与++i的不同
      • 2.5.1、对比
      • 2.5.2、图解
      • 2.6、字符串拼接
      • 2.7、小结
    • 3、代码优化
      • 3.1、尽可能使用局部变量
      • 3.2、尽量减少对变量的重复计算
      • 3.3、尽量采用懒加载的策略,即在需要的时候才创建
      • 3.4、异常不应该用来控制程序流程
      • 3.5、不要将数组声明为public static final
      • 3.6、不要创建一些不使用的对象,不要导入一些不使用的类
      • 3.7、程序原先过程中避免使用反射
      • 其他优化

1、Tomcat8优化
tomcat服务器在JavaEE项目中使用率非常高,所以在生产环境对tomcat的优化也变得非常重要了。 对于tomcat的优化,主要是从两个方面入手,第一是,tomcat自身的配置,另一个是tomcat所运行的jvm虚拟机的。

1.1、Tomcat配置优化

1.1.1、部署安装tomcat8

下载并安装 :https://tomcat.apache.org/download-80.cgi

3a184dad0f03a2f40b002f8eb88bc9c3.png

7bebff1d30bd376a2f5ccf428839a7e2.png

cbef9e504683d787b0c9217abaaab57c.png

1.1.2 禁用AJP连接

在服务状态页面中可以看到,默认状态下会启用AJP服务,并且占用8009端口。

c490d9c33052bf844dc159a27f23b406.png


什么是AJP呢?
AJP(Apache JServer Protocol)
AJPv13协议是面向包的。WEB服务器和Servlet容器通过TCP连接来交互;为了节省Socket创建的昂贵代价,WEB服务器会
尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周期过程会重用连接。

90f25261b10928efe251a18bc2cb9ae9.png


我们一般是使用Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。修改conf下的server.xml文件,将AJP
服务禁用掉即可。

f81826287fc4519c123841835bd974de.png

1.1.3、执行器(线程池)
在tomcat中每一个用户请求都是一个线程,所以可以使用线程池提高性能。 修改server.xml文件 :

e8eb3c66128aa98aa1edc12b93a041f0.png


保存退出,重启tomcat,查看效果。

ba54b8eeb7245905a53973bc163539cd.png

1.1.4 3种运行模式
tomcat的运行模式有3种 : 1. bio 默认的模式,性能非常低下,没有经过任何优化处理和支持。 2. nio 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)更好的并发运行 性能。 3. apr 安装起来最空难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能。 推荐使用nio,不过,在tomcat8中有最新的nio2,速度更快,建议使用nio2.

07474c325d80dbc5f2ba68ccb1a9c46d.png


建议tomcat8以下使用nio,tomcat8及以上使用nio2.

1.3、使用Apache JMeter进行测试

Apache JMeter是开源的压力测试工具,测量tomcat的吞吐量等信息。

1.3.1、下载安装

下载地址 :http://jmeter.apache.org/download_jmeter.cgi

e5c1d83c939294b80986d91e54394995.png


安装 :直接将下载好的zip压缩包进行解压即可。

3688ff68c4310786a997b962cf0e2063.png

1.3.2、修改主题和语言

默认的主题是黑色风格的主题并且语言是英语,这样不太方便使用,所以需要修改一下主题和中文语言。

1.3.3、创建首页的测试用例

第一步 :保存测试用例

737a2a908a5bfa988183417dd111d6c4.png


第二步 :添加线程组,使用线程模拟用户的并发

303040ef896537d13888be0ff8b8b1a1.png

32003733cde5d671434eb452d715731b.png


第四步 :添加请求监控

f560c9919fa5750fdd2d78c8d00a943d.png

1.3.4、启动、进行测试

4eb8e2fbc2db86ec6b11c5941af60d0a.png

1.3.5、聚合报告

在聚合报告中,重点看吞吐量

4361e8d95b85242d2e9380cfda1774e5.png

1.4、调整tomcat参数进行优化

通过上面测试可以看出,tomcat在不做任何调整时,吞吐量为73次/秒。

1.4.1、禁用AJP服务

b73820a1f19b2818f9129039c2cb1bda.png


可以看到,禁用AJP服务后,吞吐量会有所提升。

1.4.2、设置线程池

通过设置线程池,调整线程池相关的参数进行测试tomcat的性能。

1.4.2.1、最大线程数为500,初始为50

eead9868438eae1d711e1cf45d0d22e8.png


测试结果 :

2a8700865eddc59293a024ee364ab9d4.png


吞吐量为128次/秒。

1.4.2.2、最大线程数为1000,初始为200

934ab954de8f51a8b68ff5522e7834d6.png

c326fbeffa67cedce736f87188191bcc.png


吞吐量为151,有所提升。

1.4.2.3、最大线程数为5000,初始为1000

是否是线程数最多,速度越快呢?

89adc984085c1efea1f97d308cf4d992.png

0f9114f268ed065fed09dccbe9aeddb5.png


可以看到,虽然最大线程已经设置到5000,但是实际测试效果并不理想,并且平均的响应时间也变长, 所以单纯靠提升线程数量是不能一直得到性能提升的。

1.4.2.4、设置最大等待队列数

默认情况下,请求发送到tomcat,如果tomcat正忙,那么该请求会一直等待。这样虽然可以保证每个请求都能请求到,但是请求时间就会变长。
有些时候,我们也不一定要求请求一定等待,可以设置最大等待队列大小,如果超过就不等待了。这样虽然有些请求是失败的,但是请求时间会缩短。

a806d70ae0de9c405c08ec8ac382989f.png


测试结果 :
平均响应时间 :2.5秒;响应时间明显缩短。
错误率 :54%;错误率提升到一半,也可以理解,最大线程为500,测试的并发为1000。
吞吐量 :281次/秒;吞吐量明显提升。
结论 :响应时间、吞吐量这2个指标需要找到平衡才能达到更好的性能。

1.4.3、设置nio2的运行模式

将最大线程设置为500进行测试 :

30bb3134657ab9efdcd18987f3009801.png

801d69779271eba63c2eff6fed5fecb2.png


可以看到,平均响应时间有缩短,吞吐量有提升,可以得出结论 :nio2的性能要高于nio。

1.5、调整JVM参数进行优化

为了测试一致性,依然将最大线程数设置为500,启用nio2运行模式。

1.5.1、设置并行垃圾回收器

年轻代、老年代均使用并行收集器,初始堆内存64M,最大堆内存512M
JAVA_OPTS="-XX:+UseParallelGC -XX:UseParalleloldGC -Xms64 -Xmx512m -XX:+PrintGCDetails -XX:PringtGCTomeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:…/logs/gc.log"

04b9bcfade39bc9b5afcb0bc435d99e5.png


测试结果与默认的JVM参数结果接近。

1.5.2、查看GC日志

2b26aeb642011d83b510bcee9cbf6d6e.png


在报告中线上,在5次GC时,系统所消耗的时间大于用户时间,这反应出的服务器的性能存在瓶颈,调度CPU等资源所消耗的时间要长一些。

d432d2d5cff7a96c42f88687e8080bd0.png


可以从关键指标中看出,吞吐量表现不错,但是GC时,线程的暂停时间稍有点长。

9202c5dd065b129935de08bdb53a54f5.png


通过GC的统计可以看出 :
年轻代的gc有74次,次数稍有点多,说明年轻代设置的大小不合适需要调整;
FullGC有8次,说明堆内存的大小不合适,需要调整。

a1cebb5ed77ed031f47c068032bdcc0b.png


从GC原因可以看出,年轻代大小设置不合理,导致了多次GC。

1.5.3、调整年轻代大小

JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParalleloldGC -Xms128m -Xmx1024m -XX:NewSize=64m -XX:MaxNewSize=256m -XX:PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:…/logs/gc.log"
将初始堆大小设置为128m,最大为1024m
初始年轻代大小64m,年轻代最大256m

9fcd2763c7888c3f14bad5bbbd4d5a59.png


从测试结果来看,吞吐量以及响应时间均有提升。
查看gc日志 :

4a096fea10ce580085847ed34e4e8554.png

6ca5e3a5b024f8eb0d6cb1a44452cb16.png


可以看到GC次数要明显减少,说明调整是有效的。

1.5.4、设置G1垃圾回收器

设置最大停顿时间100毫秒,初始堆内存128m,最大堆内存1024m
JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms128m -Xmx1024m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:…/logs/gc.log"
测试结果 :

187faa1fac61dc7489788d7fb179cbd1.png


可以看到,吞吐量有所提升,响应时间也有所缩短。

1.5.5、小结

通过上述测试,可以总结出,对tomcat性能优化就是需要不断的进行调整参数,然后测试结果,可能会调优也可能会调差,这时就需要借助于gc的可视化工具来看gc的情况。再帮助我们做出决策应用调整那些参数。

2、JVM字节码

通过tomcat本身的参数以及jvm的参数对tomcat做了优化,其实要想将应用程序跑的更快、效率更高,除了对tomcat容器以及jvm优化外,应用程序代码本身如果写的效率不高,那么也是不行的,所以,对于程序本身的优化也就很重要的。
对于程序本身的优化,需要通过查看编译好的class文件中字节码。
java编写应用,需要先通过javac命令编译成class文件,再通过jvm执行,jvm执行时是需要将class文件中的字节码载入到jvm进行运行的。

2.1、通过javap命令查看class文件的字节码内容

首先,看一个简单的test1类的代码 :

da33e57ed77f2ea8de37086dd981bdb1.png


通过javap命令查看class文件中的字节码内容 :

78c5b79f9ba3d567f7fe42b509a4f337.png

5448c923dc856672c12e0b806871a1b4.png

3c0b2fdf9f2967555b8047d7b8962d0c.png

fa9729babfb4262146b351a449b12e8e.png


内容大致分为四个部分 :
第一部分 :显示了生成这个class的java源文件、版本信息、生成时间等。
第二部分 :显示了该类中所涉及到常量池,共35个常量。
第三部分 :显示该类的构造器,编译器自动插入的。
第四部分 :显示了main方法的信息。(这个需要重点关注)

2.2、常量池

5e0fba34c7e4e76baa47b9f73889b689.png

2.3、描述符

2.3.1、字段描述符

91a1fbbe228086aa39ea12b9c7d9487e.png

2.3.2、方法描述符

1986b233dd58640f337e317181a6782c.png

2.4、解读方法字节码

5ef9df037424e1b284ee6b05a3e3e43f.png

445ce1c3566d10c43852f10d1bd23c29.png

2.4.1、图解

bfd8857187cdc706e7b0bdbabbcc1427.png

2.5、研究i++与++i的不同

测试代码 :

de88ec05757e57e29cfe9bd6def521b4.png

2.5.1、对比

713c85bc0bd1734c6d748e4781b883f9.png

2.5.2、图解

8fc24c374fb856635002876d30de10b1.png

17373c86f79ec031aa9f4cde321b9911.png


区别 :
1++
只是在本地变量中对数字做了相加,并没有将数据压入到操作栈。
将前面拿到的数字1,再次从操作栈中拿到,压入到本地变量中。
++i
将本地变量中的数字做了相加,并且将数据压入到操作栈。
将操作栈中的数据,再次压入到本地变量中。
小结 :可以通过查看字节码的方式对代码的底层做研究,探究其原理。

2.6、字符串拼接

字符串的拼接在开发过程中使用是非常频繁的,常用的方式有三种 :
+号拼接 :str + “456”
StringBuilder拼接
StringBuffer拼接
StringBuffer是保证线程安全的,效率是比较低的,但是我们更多情况下是线程安全的,所以更多时候选择StringBuilder,效率会高一些。
那么,问题来了,StringBuilder和“+”号拼接,那个效率高呢?可以通话字节码的方式进行探究。
示例 :

1e84f5f3df4fc7e050b0bf8fa61f1eac.png

80f67961787f436b24ee61faf091b9c2.png


从解字节码中可以看出,m1()方法源码中是使用+号拼接,但是在字节码中也被编译成了StringBuilder方式。所以,可以得出结论,字符串拼接,+号和StringBuilder是相等的,效率一样。

e2af7c330d5fef2ce0745364472dd8d0.png

5b4a4d2c81685302d4f5548d3f56fb28.png


可以看到,m1()方法中的循环体内,每次循环都会创建StringBuilder对象,效率低于m2()方法。

2.7、小结

使用字节码的方式可以很好查看代码底层的执行,从而可以看出哪些实现效率高,哪些实现效率低。可以更好的对我们的代码做优化。让程序执行效率更高。

3、代码优化

不仅仅在运行环境进行优化,还需要在代码本身做优化,如果代码本身存在性能问题,那么在其他方面再怎么优化也不可能达到最优效果。

3.1、尽可能使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。

3.2、尽量减少对变量的重复计算

明确一个概念,对方法的调用,即使方法中只有一条语句,也是有消耗的。例如 :

75442063b5953caeedac9074c0ebeace.png


这样,在list.size()很大的时候,就减少了很多的消耗。

3.3、尽量采用懒加载的策略,即在需要的时候才创建

759edab03f8e7282fb627333ac73a395.png

3.4、异常不应该用来控制程序流程

异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,
filllnStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

3.5、不要将数组声明为public static final

因为毫无意义,这样只是定义了引用为static final,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变。

3.6、不要创建一些不使用的对象,不要导入一些不使用的类

这毫无意义,如果代码中出现“The value of local variable i is not used”、“The import java.util is never used”,那么请删除这些无用的内容。

3.7、程序原先过程中避免使用反射

反射是Java提供给用户一个很强大的功能,功能强大往往意味着效率不高。不建议在程序运行过程中使用尤其是频繁使用返回机制,特别是Method的invoke方法。
如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存。

其他优化

1、使用数据库连接池和线程池,这样可以重用对象,避免频繁地打开和关闭连接,后者可以避免频繁地创建和消耗线程。
2、容器初始化时尽可能指定长度,如 :new ArrayList<>(10);new HashMap<>(32);避免容器长度不足时,扩容带来性能损耗。
3、ArrayList随机遍历快,LinkedList添加删除快。
4、避免使用这种方式 :
Map<String, String> map = new HashMap<>();
for (String key : map.keySet()) {
String value = map.get(key);
}
尽量使用这种方式 :
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
5、不要手动调用System.gc();
6、String尽量少用正则表达式 :
正则表达式虽然功能强大,但是其效率较低,除非是有需要,否则尽可能少用。
replace() : 不支持正则
replaceAll() : 支持正则
如果仅仅是字符的替换建议使用replace();
7、日志的输出要注意级别;
8、对资源的close()建议分开操作。


http://lihuaxi.xjx100.cn/news/269278.html

相关文章

Git简介

Git简介 Git 是目前世界上最先进的分布式版本控制系统&#xff08;没有之一&#xff09; 作用 源代码管理 为什么要进行源代码管理? 方便多人协同开发方便版本控制 Git的诞生 作者是 Linux 之父&#xff1a;Linus Benedict Torvalds当初开发 Git 仅仅是为了辅助 Linux 内核…

一个故事讲完 CPU 的工作原理

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达来自 | 知乎 作者 | 柳两丛 www.zhihu.com/question/40571490/answer/718942643上二年级的小明正坐在教室里。现在是数学课&#xff0c;下午第一节&#xff0c;窗外的蝉…

jQuery实例——仿京东仿淘宝列表导航菜单

以前看着京东&#xff0c;淘宝的导航做的真好&#xff0c;真想哪一天自己也能做出来这么漂亮功能全的导航菜单。今天弄了一下午终于自制成功&#xff0c;主要使用jQuery和CSS&#xff0c;实现功能基本和京东一样。 功能介绍&#xff1a; 1、鼠标停留导航&#xff1b; 2、根据子…

实战:人脸识别实战项目(源码共享)

首先我想问个问题&#xff1a;现在什么工程师最值钱&#xff1f;毫无疑问&#xff0c;我想超 90% 的都会说&#xff1a;人工智能工程师。也难怪&#xff0c;随着近几年人工智能的发展&#xff0c;已经逐渐渗透到了各个领域&#xff0c;比如&#xff1a;医疗、教育、机械自动化、…

工作流入门链接

百度百科-工作流 http://baike.baidu.com/link?urlZjElBNByyZz_ItLtd_Uqt3Sadcwv0-4CDO806vKQWJDuUOFybbkzpg8GOB1EU71w8bT4x64RoRXBrFXa7o_dK 企业应用工作流的好处http://jingyan.baidu.com/article/90895e0fe9c56164ec6b0b24.html工作流管理的好处http://blog.sina.com.cn/…

Scala之泛型类

2019独角兽企业重金招聘Python工程师标准>>> 1. 泛型类 带有一个或多个类型参数的类是泛型的。 泛型类的定义&#xff1a; //带有类型参数A的类定义class Stack[A] {private var elements: List[A] Nil//泛型方法def push(x: A) { elements x :: elements }def pe…

看论文不用来回翻了,这款PDF阅读神器能自动提取前文信息,科研效率max!

点击上方“视学算法”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达丰色 发自 凹非寺量子位 报道 | 公众号 QbitAI在阅读学术论文PDF时&#xff0c;各位是不是经常需要来回翻页查找某个专业术语或数学公式&#xff1f;满篇密密麻麻的数字与大…

数据库的介绍、分类、作用和特点

1. 数据库的介绍 数据库就是存储和管理数据的仓库&#xff0c;数据按照一定的格式进行存储&#xff0c;用户可以对数据库中的数据进行增加、修改、删除、查询等操作。 【在数据库中输错&#xff0c;按住“ctrlc”重新输入】 【之前基础用文件来存储的&#xff0c;数据库比文…