1 tomcat8优化
1.1 tomcat配置优化
部署安装
1.1.1 修改配置文件,配置tomcat的管理用户
# vi apache-tomcat-8.5.34/conf/tomcat-users.xml
<role rolename="manager"/>
<role rolename="manager-gui"/>
<role rolename="admin"/>
<role rolename="admin-gui"/>
<user username="tomcat" password="maggie" roles="admin-gui,admin,manager-gui,manager"/>
#tomcat8还需要配置
vi webapps/manager/META-INF/context.xml
将value的内容注释掉。
<Context antiResourceLocking="false" privileged="true" >
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
<Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>
# 启动tomcat
cd apache-tomcat-8.5.34/bin/
./startup.sh && tail -f ../logs/catalina.out
# 打开浏览器访问
http://116.62.108.24:8080
1.1.2 禁用AJP连接
修改server.xml,注释下面这行
<!-- Define an AJP 1.3 Connector on port 8009 -->
<!--<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
1.1.3 执行器(线程池)
tomcat中没一个用户请求都是一个线程,可以使用线程池提高性能。
修改server.xml文件
# 放开注释
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true" maxQueueSize="100"/>
<!--
参数说明:
maxThreads:最大并发数,默认为200,一般建议在500~1000,根据硬件设施和业务来判断
minSpareThreads: Tomcat初始化时创建的线程数,默认设置25
prestartminSpareThreads:在tomcat初始化的时候就初始化minSpareThreads参数,如果不等于true,minSpareThreads值就没啥效果
maxQueueSize:最大的等待队列数,超过则拒绝请求
-->
...
# 放开注释引用上面的执行器
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
1.1.4 三种运行模式
设置nio2
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443" />
1.2 部署测试用的java web项目
1.2.1 执行 dashboard.sql
cat dashboard.sql | mysql -h rm-bp14byw6s6kc846nl125010.mysql.rds.aliyuncs.com -P 3306 -u zephyr -p
1.2.2 部署web应用
cd servers/apache-tomcat-8.5.34/webapps
rm -rf *
mkdir ROOT
cd ROOT
rz
jar -xvf itcat-dashboard-web.war
rm -rf itcat-dashboard-web.war
# 修改数据库配置文件
cd /apache-tomcat-8.5.34/webapps/ROOT/WEB-INF/classs
vi jdbc.propertise
# 修改数据库配置
# 重启服务
1.3 使用Apache Jmeter进行测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-seRr3nEM-1588240638556)(D:\exquisite\mynote\jvm\images\image-20200417100400573.png)]
1.4 调整tomcat参数进行优化
1.4.1 禁用AJP服务
#修改server.xml,注释下面这行
<!-- Define an AJP 1.3 Connector on port 8009 -->
<!--<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
可以看到禁用ajp后,吞吐量有提升。
1.4.2 设置线程池
1.4.2.1 最大线程数为500,初始为50
server.xml
# 设置线程池
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true"/>
# 放开注释引用上面的执行器,这里要删除上一个重复的connector
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443" />
1.4.2.2 最大线程数为1000,初始为200
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="1000" minSpareThreads="200" prestartminSpareThreads="true"/>
1.4.2.3 最大线程数为5000,初始为1000
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="5000" minSpareThreads="1000" prestartminSpareThreads="true"/>
1.4.2.4 设置最大等待队列
#设置等待队列
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="100" prestartminSpareThreads="true" maxQueueSize="100"/>
1.4.3 设置nio2的运行模式
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true" maxQueueSize="100"/>
# 设置nio2
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443" />
1.5 调整JVM参数进行优化
在最大线程为500,启用nio2的模式运行的前提下继续优化。
1.5.1 设置并行垃圾回收器
# vi bin/catalina.sh
# 年轻代、老年代均使用并行垃圾收集器,初始堆内存64M,最大堆内存512M
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx512m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/export/servers/tomcat/logs/gc.log"
# 这里配置log路径为全路径即可,配置为相对路径(-Xloggc:../logs/gc.log)未能生成文件
# 打印的日志增加时间戳
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx512m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/export/servers/tomcat/logs/gc-`date +%Y%m%d%H%M`.log"
1.5.2 查看gc日志文件
网站在线分析gc.log http://gceasy.io
若查看到 System Time greater than User Time
- 系统消耗的时间大于用户时间,反映出服务器性能存在瓶颈,调度CPU等资源消耗时间长
- Throughput 吞吐量和Avg Pause GC Time、Max Pause GC Time (线程暂停时间)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lo8uWw7d-1588240638560)(D:\exquisite\mynote\jvm\images\image-20200418211332210.png)]
- Total GC stats GC的总统计) Minor GC stats(年轻代GC统计) Full GC stats (FullGC统计)GC Pause Statistics (GC时暂停统计)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-70xa5U7N-1588240638562)(D:\exquisite\mynote\jvm\images\image-20200418211531383.png)]
- 触发GC的原因
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fx1ntFq-1588240638566)(D:\exquisite\mynote\jvm\images\image-20200418213112111.png)]
数据解释:
# jdk8中永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关;
# JDK8中,XX:MaxMetaspaceSize确实是没有上限的,最大容量与机器的内存有关;但是XX:MetaspaceSize是有一个默认值的:21M,设置一个XX:MetaspaceSize的JVM启动参数:-XX:MetaspaceSize=128M
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms64m -Xmx512m -XX:MetaspaceSize=128M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/export/servers/tomcat/logs/gc.log"
设置一个XX:MetaspaceSize的JVM启动参数:-XX:MetaspaceSize=128M
如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5JTZgeH6-1588240638569)(D:\exquisite\mynote\jvm\images\image-20200418214641014.png)]
Ergonomics翻译成中文,一般都是“人体工程学”。在JVM中的垃圾收集器中的Ergonomics就是负责自动的调解gc暂停时间和吞吐量之间的平衡,然后你的虚拟机性能更好的一种做法。
1.5.3 调整年轻代大小
# 将初始堆大小设置为128m,最大为1024m, 初始年轻代大小为64m,年轻代最大256m
# vi bin/catalina.sh
# -xx:NewSize=64m 我的阿里云,这样设置会报错:Unrecognized option: -xx:NewSize=64m
# -Xmn64m 设置年轻代大小的方式
JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms128m -Xmx1024m -Xmn64m -XX:MaxNewSize=256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/export/servers/tomcat/logs/gc.log"
看到GC次数要明显减少,说明调整有效
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qANcc9Pq-1588240638570)(D:\exquisite\mynote\jvm\images\image-20200418220910722.png)]
1.5.4 设置G1垃圾回收器
# vi bin/catalina.sh
# 年轻代、老年代均使用并行垃圾收集器,初始堆内存64M,最大堆内存512M
JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms128m -Xmx1024m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/export/servers/tomcat/logs/gc.log"
# 吞吐量提升,平均响应时间缩短
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcFueDCg-1588240638571)(D:\exquisite\mynote\jvm\images\image-20200418221013279.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdCQ5Yv3-1588240638573)(D:\exquisite\mynote\jvm\images\image-20200418221930146.png)]
1.5.5 小结
不断的调整测试,可能调差也可能调好,gc在easy,io中查看回收情况。
2 JVM字节码
2.1 通过javap命令查看class文件的字节码
public class Test1 {
public static void main(String[] args) {
int a = 2;
int b = 5;
int c = b - a;
System.out.println(c);
}
}
通过javap命令查看class文件中的字节码内容:
javac Test1.java
javap -v Test1.class > Test1.txt
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IMlHBsQu-1588240638574)(D:\exquisite\mynote\jvm\images\image-20200418222244804.png)]
Classfile /D:/exquisite/code/lf-jvm/jvm-test/src/main/java/cn/zephyr/jvm/Test1.class
Last modified 2020-4-18; size 414 bytes
MD5 checksum 9d961ef8b964df7db9afbc014472dd41
Compiled from "Test1.java"
public class cn.zephyr.jvm.Test1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#14 // java/lang/Object."<init>":()V
#2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #17.#18 // java/io/PrintStream.println:(I)V
#4 = Class #19 // cn/zephyr/jvm/Test1
#5 = Class #20 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 SourceFile
#13 = Utf8 Test1.java
#14 = NameAndType #6:#7 // "<init>":()V
#15 = Class #21 // java/lang/System
#16 = NameAndType #22:#23 // out:Ljava/io/PrintStream;
#17 = Class #24 // java/io/PrintStream
#18 = NameAndType #25:#26 // println:(I)V
#19 = Utf8 cn/zephyr/jvm/Test1
#20 = Utf8 java/lang/Object
#21 = Utf8 java/lang/System
#22 = Utf8 out
#23 = Utf8 Ljava/io/PrintStream;
#24 = Utf8 java/io/PrintStream
#25 = Utf8 println
#26 = Utf8 (I)V
{
public cn.zephyr.jvm.Test1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_2
1: istore_1
2: iconst_5
3: istore_2
4: iload_2
5: iload_1
6: isub
7: istore_3
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_3
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: return
LineNumberTable:
line 6: 0
line 7: 2
line 8: 4
line 9: 8
line 10: 15
}
SourceFile: "Test1.java"
2.2 常量池
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d3WPVNbI-1588240638575)(D:\exquisite\mynote\jvm\images\image-20200418222637638.png)]
2.3 字段描述符
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJHhRdCF-1588240638576)(D:\exquisite\mynote\jvm\images\image-20200418222803495.png)]
2.4 方法描述符
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K4tC1FXe-1588240638577)(D:\exquisite\mynote\jvm\images\image-20200418223102753.png)]
2.4 解读方法字节码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHvcVQ7U-1588240638578)(D:\exquisite\mynote\jvm\images\image-20200418223607676.png)]
3 代码优化
-
尽可能使用局部变量
-
尽量减少对变量的重复计算
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4WANxrna-1588240638578)(D:\exquisite\mynote\jvm\images\image-20200418223743823.png)]
-
尽量采用懒加载的策略,即在需要的时候才创建对象
-
异常不应该用来控制程序流程
-
不要将数组声明为putlic static final
-
不要创建一些不使用的对象,不要导入一些不使用的类
-
程序运行过程中避免使用反射
-
使用数据库连接池和线程池
-
容器初始化时候尽可能指定长度
- new ArrayList<>(10)
-
ArrayList随机遍历快,LinkedList添加删除快
-
使用Entry遍历Map
-
不要手动调用System.gc()
-
String 尽量少用正则表达式
-
日志输出要注意级别
-
对资源的close建议分开操作
-
尽量减少对变量的重复计算
[外链图片转存中…(img-4WANxrna-1588240638578)]
-
尽量采用懒加载的策略,即在需要的时候才创建对象
-
异常不应该用来控制程序流程
-
不要将数组声明为putlic static final
-
不要创建一些不使用的对象,不要导入一些不使用的类
-
程序运行过程中避免使用反射
-
使用数据库连接池和线程池
-
容器初始化时候尽可能指定长度
- new ArrayList<>(10)
-
ArrayList随机遍历快,LinkedList添加删除快
-
使用Entry遍历Map
-
不要手动调用System.gc()
-
String 尽量少用正则表达式
-
日志输出要注意级别
-
对资源的close建议分开操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-icCLHbGa-1588240638580)(D:\exquisite\mynote\jvm\images\image-20200418224150231.png)]