1.JVM介绍
JVM是Java Virtual Machine(Java虚拟机)的缩写
Java虚拟机本质是就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。
2.Tomcat介绍
a.什么是Tomcat
Tomcat和我们此前学习的 Nginx 类似,也是一个Web服务器。
b.Tomcat与Nginx有什么区别?
Nginx仅支持静态资源,而Tomcat则支持Java开发的 jsp 动态资源和静态资源。
Nginx适合做前端负载均衡,而Tomcat适合做后端应用服务处理。
通常情况下,企业会使用 Nginx+tomcat 结合使用,由Nginx处理静态资源,Tomcat处理动态资源。
3.Tomcat快速安装
方法1:
rpm -ivh jdk-8u102-linux-x64.rpm
mkdir /app
tar xf apache-tomcat-8.0.27.tar.gz -C /app
/app/apache-tomcat-8.0.27/bin/startup.sh
方法2:
mkdir /app/
tar xf jdk-8u60-linux-x64.tar.gz -C /app/
ln -s /app/jdk1.8.0_60 /app/jdk
sed -i.ori '$a export JAVA_HOME=/app/jdk\nexport PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH\nexport CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar' /etc/profile
source /etc/profile
tar xf apache-tomcat-8.0.27.tar.gz -C /app
/app/apache-tomcat-8.0.27/bin/startup.sh
4.Tomcat启动慢解决方案
没优化之前启动时间
[root@tomcat logs]# grep 'Server startup' catalina.out
03-Aug-2019 03:15:18.225 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 591050 ms
优化之后启动时间
[root@tomcat logs]# grep 'Server startup' catalina.out
03-Aug-2019 03:15:18.225 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 591050 ms
03-Aug-2019 03:22:14.112 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1326 ms
优化方法:
vi /usr/java/jdk1.8.0_102/jre/lib/security/java.security
securerandom.source=file:/dev/urandom
5.tomcat目录结构介绍
[root@tomcat apache-tomcat-8.0.27]# ll
total 92
drwxr-xr-x 2 root root 4096 Aug 3 03:05 bin #主要包含启动、关闭tomcat脚本和脚本依赖文件
drwxr-xr-x 3 root root 198 Aug 3 03:05 conf #tomcat配置文件目录
drwxr-xr-x 2 root root 4096 Aug 3 03:05 lib #tomcat运行需要加载的jar包
-rw-r--r-- 1 root root 57011 Sep 28 2015 LICENSE #license文件,不重要
drwxr-xr-x 2 root root 197 Aug 3 03:15 logs #在运行过程中产生的日志文件
-rw-r--r-- 1 root root 1444 Sep 28 2015 NOTICE #不重要
-rw-r--r-- 1 root root 6741 Sep 28 2015 RELEASE-NOTES #版本特性,不重要
-rw-r--r-- 1 root root 16204 Sep 28 2015 RUNNING.txt #帮助文件,不重要
drwxr-xr-x 2 root root 30 Aug 3 03:05 temp #存放临时文件
drwxr-xr-x 7 root root 81 Sep 28 2015 webapps #站点目录
drwxr-xr-x 3 root root 22 Aug 3 03:05 work #tomcat运行时产生的缓存文件
6.tomcat配置文件
https://blog.csdn.net/yes_is_ok/article/details/82992429
#多站点配置文件
vim /app/apache-tomcat-8.0.27/conf/server.xml
查找host 并复制 去除注释的字段
cp -a /app/apache-tomcat-8.0.27/webappas/* /app/apache-tomcat-8.0.27/webappas2
7.Tomcat部署zrlog
上传代码加启动
8.配置tomcat basic认证
vim /app/apache-tomcat-8.0.27/webapps/ROOT/WEB-INF/web.xml
<web-app>
......
<security-constraint>
<web-resource-collection>
<web-resource-name>test</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>test100</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Default</realm-name>
</login-config>
</web-app>
# 添加系统角色
vim /app/apache-tomcat-8.0.27/conf/tomcat-users.xml
<role rolename="manager-gui"/>
<role rolename="test100"/>
<user username="tomcat" password="123456" roles="manager-gui,test100"/>
#重启tomcat生效
9.tomcat https配置文件
vim /etc/nginx/nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream tom {
server 10.0.0.7:8080;
server 10.0.0.41:8080;
}
server {
listen 443 ssl;
server_name blog.oldqiang.com;
ssl_certificate /root/ssl_key/Nginx/1_blog.oldqiang.com_bundle.crt;
ssl_certificate_key /root/ssl_key/Nginx/2_blog.oldqiang.com.key;
location / {
proxy_pass http://tom;
proxy_set_header Host $http_host;
}
}
server {
listen 80;
server_name blog.oldqiang.com;
return 302 https://$server_name$request_uri;
}
}
10.tomcat 实现读写分离
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream tom {
server 10.0.0.7:8080;
server 10.0.0.41:8080;
}
upstream tom2{
server 10.0.0.7:8080;
}
server {
listen 443 ssl;
server_name blog.oldqiang.com;
ssl_certificate /root/ssl_key/Nginx/1_blog.oldqiang.com_bundle.crt;
ssl_certificate_key /root/ssl_key/Nginx/2_blog.oldqiang.com.key;
location / {
proxy_set_header Host $http_host;
#读写分离判断
if ( $request_method = POST ){
proxy_pass http://tom2;
}
proxy_pass http://tom;
}
#动静分离
location ~.*\.(png|jpg) {
root /data;
}
}
server {
listen 80;
server_name blog.oldqiang.com;
return 302 https://$server_name$request_uri;
}
}
11.使用maven编译java项目
1.安装maven,二进制
下载maven
wget http://192.168.13.120/191118/apache-maven-3.6.1-bin.tar.gz
解压
tar xf apache-maven-3.6.1-bin.tar.gz -C /usr/local
ln -s /usr/local/apache-maven-3.6.1/ /usr/local/maven
给maven配置环境变量
vim /etc/profile
...
export M2_HOME=/usr/local/maven
export PATH=${M2_HOME}/bin:$PATH
source /etc/profile</pre>
2: java项目的源码
wget http://192.168.13.120/191118/SpringBootWeb.tar.gz
tar xf SpringBootWeb.tar.gz
SpringBoot java开发框架
3:使用maven编译打包
cd SpringBootWeb
maven源加速:
vim /usr/local/maven/conf/settings.xml
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
mvn clean package 先清理缓存文件,再打包
mvn package 打包</pre>
4:部署war包
cp target/SpringBootWeb.war /app/apache-tomcat-8.0.27/webapps2/
5: 测试访问
浏览器访问:http://session.oldqiang.com/SpringBootWeb/
12.tomcat监控自定义
客户端
vim /application/apache-tomcat-8.0.27/bin/catalina.sh
CATALINA_OPTS="$CATALINA_OPTS
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=10.0.0.100
-Dcom.sun.management.jmxremote.port=12345
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
重启tomcat服务
zabbix 端
#安装zabbix-java-gateway
yum install zabbix-java-gateway.x86_64 -y
#配置zabbix-server和zabbix-java-gateway
vim /etc/zabbix/zabbix_java_gateway.conf
START_POLLERS=5
vim /etc/zabbix/zabbix_server.conf
JavaGateway=127.0.0.1
JavaGatewayPort=10052
StartJavaPollers=5
#启动服务
systemctl restart zabbix-java-gateway.service
systemctl restart zabbix-server.service
13.tomcat调优化
#调优配置解释
[https://oldqiang.com/archives/435.html](https://oldqiang.com/archives/435.html)
请看一下一个时间的Java参数配置:(服务器:Linux 64Bit,8Core×16G)
JAVA_OPTS="$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/aaa/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize=1G -XX:MaxNewSize=1G"
1. ## 参数说明
-server -Xmx3g -Xms3g -XX:MaxPermSize=128m
-XX:NewRatio=2 # eden/old 的比例
-XX:SurvivorRatio=8 # s/e的比例
-XX:+UseParallelGC
-XX:ParallelGCThreads=8
-XX:+UseParallelOldGC # 这个是JAVA 6出现的参数选项
-XX:LargePageSizeInBytes=128m # 内存页的大小, 不可设置过大,会影响Perm的大小
-XX:+UseFastAccessorMethods # 原始类型的快速优化
-XX:+DisableExplicitGC # 关闭System.gc()
-Xss # 是线程栈的大小
另外 -Xss 是线程栈的大小, 这个参数需要严格的测试,一般小的应用,如果栈不是很深, 应该是128k够用的,不过,我们的应用调用深度比较大,还需要做详细的测试。这个选项对性能的影响比较大。**建议使用****256K****的大小**。
1. ## 常见配置汇总
1. ### 堆设置
**-Xms**:初始堆大小
**-Xmx**:最大堆大小
**-XX:NewSize=n**:设置年轻代大小
**-XX:NewRatio=n**:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
**-XX:SurvivorRatio=n**:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
**-XX:MaxPermSize=n**:设置持久代大小
1. ### 收集器设置
**-XX:+UseSerialGC**:设置串行收集器
**-XX:+UseParallelGC**:设置并行收集器
**-XX:+UseParalledlOldGC**:设置并行年老代收集器
**-XX:+UseConcMarkSweepGC**:设置并发收集器
1. ### 垃圾回收统计信息
**-XX:+PrintGC**
**-XX:+PrintGCDetails**
**-XX:+PrintGCTimeStamps**
**-Xloggc:filename**
1. ### 并行收集器设置
**-XX:ParallelGCThreads=n**:设置并行收集器收集时使用的CPU数。并行收集线程数。
**-XX:MaxGCPauseMillis=n**:设置并行收集最大暂停时间
**-XX:GCTimeRatio=n**:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
1. ### 并发收集器设置
**-XX:+CMSIncrementalMode**:设置为增量模式。适用于单CPU情况。
**-XX:ParallelGCThreads=n**:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
1. ## 调优方法
一切都是为了这一步,**调优**,在调优之前,我们需要记住下面的**原则**:
1、多数的Java应用不需要在服务器上进行GC优化;
2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;
3、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);
4、减少创建对象的数量;
5、减少使用全局变量和大对象;
6、GC优化是到最后不得已才采用的手段;
7、在实际使用中,分析GC情况优化代码比优化GC参数要多得多;
GC优化的目的有两个([](http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)[http://www.360doc.com/content/13/0305/10/15643_269388816.shtml ):](http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)
[**1、将转移到老年代的对象数量降低到最小;**
**2、减少full GC的执行时间;**
为了达到上面的目的,一般地,你需要做的事情有:
1、减少使用全局变量和大对象;
2、调整新生代的大小到最合适;
3、设置老年代的大小为最合适;
4、选择合适的GC收集器;
在上面的4条方法中,用了几个"合适",那究竟什么才算合适,一般的,请参考上面"收集器搭配"和"启动内存分配"两节中的建议。但这些建议不是万能的,需要根据您的机器和应用情况进行发展和变化,实际操作中,可以将两台机器分别设置成不同的GC参数,并且进行对比,选用那些确实提高了性能或减少了GC时间的参数。
真正熟练的使用GC调优,是建立在多次进行GC监控和调优的实战经验上的,进行监控和调优的一般步骤为:
1. **监控GC的状态**
使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化;
1. **分析结果,判断是否需要优化**
如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;
注:**如果满足下面的指标,则一般不需要进行****GC**:
Minor GC执行时间不到50ms;
Minor GC执行不频繁,约10秒一次;
**Full GC****执行时间不到1s;**
Full GC执行频率不算频繁,不低于10分钟1次;
1. **调整GC类型和内存分配**
如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择;
1. **不断的分析和调整**
通过不断的试验和试错,分析并找到最合适的参数
1. **全面应用参数**
如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。
1. ## 调优实例
上面的内容都是纸上谈兵,下面我们以一些真实例子来进行说明。
1. ### 实例1
笔者昨日发现部分开发测试机器出现异常:
java.lang.OutOfMemoryError: GC overhead limit exceeded
这个异常代表:GC为了释放很小的空间却耗费了太多的时间,其原因一般有两个
1. 堆太小
2. 有死循环或大对象
笔者首先排除了第2个原因,因为这个应用同时是在线上运行的,如果有问题,早就挂了。所以怀疑是这台机器中堆设置太小;
使用**ps -ef |grep "java"** 查看,发现:
![image](https://upload-images.jianshu.io/upload_images/19559641-08743e64d64a4d25.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
该应用的堆区设置只有768m,而机器内存有2g,机器上只跑这一个java应用,没有其他需要占用内存的地方。另外,这个应用比较大,需要占用的内存也比较多;
笔者通过上面的情况判断,只需要改变堆中各区域的大小设置即可,于是改成下面的情况:
![image](https://upload-images.jianshu.io/upload_images/19559641-3fdb506dee30accd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
跟踪运行情况发现,相关异常没有再出现;
1. ### 实例2](http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)
[](http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)[http://www.360doc.com/content/13/0305/10/15643_269388816.shtml](http://www.360doc.com/content/13/0305/10/15643_269388816.shtml)
一个服务系统,**经常出现卡顿,分析原因,发现****Full GC****时间太长**:
jstat -gcutil:
S0 S1 E O P YGC YGCT FGC FGCT GCT
12.16 0.00 5.18 63.78 20.32 54 2.047 5 6.946 8.993
分析上面的数据,发现Young GC执行了54次,耗时2.047秒,每次Young GC耗时37ms,在正常范围,**而****Full GC执行了5次,耗时6.946秒,每次平均1.389s,数据显示出来的问题是:Full GC****耗时较长**,分析该系统的是指发现,NewRatio=9,也就是说,新生代和老生代大小之比为1:9,这就是问题的原因:
1. 新生代太小,导致对象提前进入老年代,触发老年代发生Full GC;
2. 老年代较大,进行Full GC时耗时较大;
优化的方法是调整NewRatio的值,调整到4,发现Full GC没有再发生,只有Young GC在执行。这就是把对象控制在新生代就清理掉,没有进入老年代(这种做法对一些应用是很有用的,但并不是对所有应用都要这么做)
1. ### 实例3
一应用在性能测试过程中,发现内存占用率很高,Full GC频繁,使用sudo -u admin -H jmap -dump:format=b,file=文件名.hprof pid 来dump内存,生成dump文件,并使用Eclipse下的mat差距进行分析,发现:
![image](https://upload-images.jianshu.io/upload_images/19559641-36f4aa036b856b8c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
从图中可以看出,这个线程存在问题,队列**LinkedBlockingQueue**所引用的大量对象并未释放,导致整个线程占用内存高达378m,此时通知开发人员进行代码优化,将相关对象释放掉即可。
1. ## 调优总结
1. ### 年轻代大小选择
* **响应时间优先的应用**:**尽可能设大,直到接近系统的最低响应时间限制**(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
* **吞吐量优先的应用**:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
1. ### 年老代大小选择
* **响应时间优先的应用**:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑**并发会话率**和**会话持续时间**等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
* 并发垃圾收集信息
* 持久代并发收集次数
* 传统GC信息
* 花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
* **吞吐量优先的应用**:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
1. ### 较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现"碎片",如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现"碎片",可能需要进行如下配置:
* **-XX:+UseCMSCompactAtFullCollection**:使用并发收集器时,开启对年老代的压缩。
* **-XX:CMSFullGCsBeforeCompaction=0**:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩。
**参考资料:**
http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html