jstack(线程、死循环、死锁)
JVisualVM(本地和远程可视化监控)
使用 BTrace 进行拦截调试
Tomcat 性能监控与调优
Nginx 性能监控与调优
JVM 层 GC 调优
JAVA代码层调优
1.JVM的参数类型
标准参数(各版本中保持稳定)
-help
-server -client
-version -showversion
-cp -classpath
X 参数(非标准化参数)
-Xint:解释执行
-Xcomp:第一次使用就编译成本地代码
-Xmixed:混合模式,JVM 自己决定是否编译成本地代码
示例:
java -version(默认是混合模式)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)
java -Xint -version
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, interpreted mode)
XX 参数(非标准化参数)
主要用于 JVM调优和 debug
Boolean类型(+-)
格式:-XX:[+-]<name>表示启用或禁用 name 属性
如:-XX:+UseConcMarkSweepGC(启用cms垃圾收集器)
-XX:+UseG1GC(启用G1垃圾收集器)
非Boolean类型(key-value)(带=的)
格式:-XX:<name>=<value>表示 name 属性的值是 value
如:-XX:MaxGCPauseMillis=500(GC最大停用时间)
-xx:GCTimeRatio=19
-Xmx -Xms属于 XX 参数
-Xms 等价于-XX:InitialHeapSize(初始化堆大小)
-Xmx 等价于-XX:MaxHeapSize (最大堆大小)
-xss 等价于-XX:ThreadStackSize(线程堆栈)
查看
jinfo -flag MaxHeapSize <pid>(查看最大内存)
-XX:+PrintFlagsInitial
-XX:+PrintFlagsFinal
-XX:+UnlockExperimentalVMOptions 解锁实验参数
-XX:+UnlockDiagnosticVMOptions 解锁诊断参数
-XX:+PrintCommandLineFlags 打印命令行参数
输出结果中=表示默认值,:=表示被用户或 JVM 修改后的值
示例:java -XX:+PrintFlagsFinal -version
2.jinfo & jps(参数和进程查看)
pid 可通过类似 ps -ef|grep tomcat或 jps来进行查看
jps
[详情参考 jps官方文档](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html)
-l
jinfo
jinfo -flag MaxHeapSize <pid>
jinfo -flags <pid>
jstat
[详情参考 jps官方文档](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html#BEHBBBDJ)
jstat 使用示例
3.jstat(类加载、垃圾收集、JIT 编译)
类加载
每隔1000ms 即1秒,共输出10次
jstat -class <pid> 1000 10
loaded 加载类的个数
[[email protected] java]# jps
4167 Jps
3370 Bootstrap
[[email protected] java]# jstat -class 3370
Loaded Bytes Unloaded Bytes Time
5990 12028.7 0 0.0 15.50
垃圾收集
[[email protected] java]# jstat -gc 3370
Warning: Unresolved Symbol: sun.gc.metaspace.capacity substituted NaN
Warning: Unresolved Symbol: sun.gc.metaspace.used substituted NaN
Warning: Unresolved Symbol: sun.gc.compressedclassspace.capacity substituted NaN
Warning: Unresolved Symbol: sun.gc.compressedclassspace.used substituted NaN
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1600.0 1600.0 0.0 1600.0 13184.0 7779.2 32696.0 22669.8 - - - - 117 1.056 5 0.386 1.441
-gc, -gcutil, -gccause, -gcnew, -gcold
jstat -gc <pid> 1000 10
以下大小的单位均为 KB
S0C, S1C, S0U, S1U: S0和 S1的总量和使用量
EC, EU: Eden区总量与使用量
OC, OU: Old区总量与使用量
MC, MU: Metacspace区(jdk1.8前为 PermGen)总量与使用量
CCSC, CCSU: 压缩类区总量与使用量
YGC, YGCT: YoungGC 的次数与时间
FGC, FGCT: FullGC 的次数与时间
GCT: 总的 GC 时间
JIT 编译
-compiler, -printcompilation
[[email protected] java]# jstat -compiler 3370
Compiled Failed Invalid Time FailedType FailedMethod
833 0 0 26.04 0
花费26.04s编译了833个方法
4.jmap+MAT(内存溢出)
详情参考jmap 官方文档
内存溢出演示:
https://start.spring.io/生成初始代码
最终代码:monitor_tuning
为快速产生内存溢出,右击 Run As>Run Configurations, Arguments 标签VM arguments 中填入
-Xmx32M -Xms32M
访问 http://localhost:8080/heap
Exception in thread “http-nio-8080-exec-2” Exception in thread “http-nio-8080-exec-1” java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space
-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M(同时在 pom.xml 中加入 asm 的依赖)
访问 http://localhost:8080/nonheap
Exception in thread “main” java.lang.OutOfMemoryError: Metaspace
Exception in thread “ContainerBackgroundProcessor[StandardEngine[Tomcat]]” java.lang.OutOfMemoryError: Metaspace
内存溢出自动导出
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./
右击 Run As>Run Configurations, Arguments 标签VM arguments 中填入
-Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
可以看到自动在当前目录中生成了一个java_pid660.hprof文件
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to ./java_pid660.hprof …
另一种导出溢出也更推荐的方式是jmap
option: -heap, -clstats, -dump:, -F
jmap -dump:format=b,file=heap.hprof
jmap 导出溢出文件
MAT下载地址:http://www.eclipse.org/mat/
找开上述导出的内存溢出文件即可进行分析,如下图的溢出源头分析:
Memory Analyzer 内存溢出分析
jstack
详情参考 jstack 官方文档
jstack
可查看其中包含java.lang.Thread.State: WAITING (parking),JAVA 线程包含的状态有:
NEW:线程尚未启动
RUNNABLE:线程正在 JVM 中执行
BLOCKED:线程在等待监控锁(monitor lock)
WAITING:线程在等待另一个线程进行特定操作(时间不确定)
TIMED_WAITING:线程等待另一个线程进行限时操作
TERMINATED:线程已退出
monitor_tuning中新增CpuController.java
mvn clean package -Dmaven.test.skip
mvn 打包提速参考 CSDN
此时会生成一个monitor_tuning-0.0.1-SNAPSHOT.jar的 jar包,为避免本地的 CPU 消耗过多导致死机,建议上传上传到虚拟机进行测试
nohup java -jar monitor_tuning-0.0.1-SNAPSHOT.jar &
访问 http://xx.xx.xx.xx:12345/loop(端口12345在application.properties文件中定义)
top -p -H可以查看线程及 CPU 消耗情况
top 命令打出线程 CPU 消耗
使用 jstack 可以导出追踪文件,文件中 PID 在 jstack 中显示的对应 nid 为十六进制(命令行可执行 print ‘%x’ 可以进行转化,如1640对应的十六进制为668)
“http-nio-12345-exec-3” #18 daemon prio=5 os_prio=0 tid=0x00007f10003fb000 nid=0x668 runnable [0x00007f0fcf8f9000]
java.lang.Thread.State: RUNNABLE
at org.alanhou.monitor_tuning.chapter2.CpuController.getPartneridsFromJson(CpuController.java:77)
…
访问http://xx.xx.xx.xx:12345/deadlock(如上jstack 导出追踪记录会发现如下这样的记录)
Java stack information for the threads listed above:
“Thread-5”:
at org.alanhou.monitor_tuning.chapter2.CpuController.lambda$deadlock$Lambdadeadlock$Lambda$336/1704575158.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
JVisualVM
详情参考官方文档
Mac命令行直接输入jvisualvm命令,Windows 找到对应的 exe 文件双击即可打开
插件安装Tools>Plugins>Settings根据自身版本(java -version)更新插件中心地址,各版本查询地址:
http://visualvm.github.io/pluginscenters.html
建议安装:Visual GC, BTrace Workbench
VisualVM
以上是本地的JAVA进程监控,还可以进行远程的监控,在上图左侧导航的 Applications 下的 Remote 处右击Add Remote Host…,输入主机 IP 即可添加,在 IP 上右击会发现有两种连接 JAVA 进程进行监控的方式:JMX, jstatd
bin/catalina.sh(以192.168.0.5为例)
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=192.168.0.5"
启动tomcat,以 JMX 为例,在 IP 上右击点击Add JMX Connection…,输入 IP:PORT
Add JMX Connection
以上为 Tomcat,其它 JAVA 进程也是类似的,如:
nohup java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9005 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=192.168.0.5 -jar monitor_tuning-0.0.1-SNAPSHOT.jar &
BTrace
BTrace 可以动态地向目标应用程序的字节码注入追踪代码,使用的技术有 JavaCompilerApi, JVMTI, Agent, Instrumentation+ASM
使用方法:JVisualVM中添加 BTrace 插件
方法二:btrace <trace_script>
monitor_tuning中新增包org.alanhou.monitor_tuning.chapter4
安装BTrace 要记得配置环境变量,以 Mac 为例
vi ~/.bash_profile
BTRACE_HOME=/Applications/btrace
PATH=BTRACE_HOME/bin
export PATH
source ~/.bash_profile
pom.xml 中添加 btrace-agent, btrace-boot, btrace-client的依赖
访问:http://localhost:12345/ch4/arg1?name=Java
示例输出
192:chapter4 alan$ btrace 2247 PrintArgSimple.java
[Java, ]
org.alanhou.monitor_tuning.chapter4.Ch4Controller,arg1
常见问题:Please set JAVA_HOME before running this script
vi ~/.bash_profile
export JAVA_HOME=$(/usr/libexec/java_home)
source ~/.bash_profile
拦截方法
普通方法:@OnMethod( clazz="", method=""),如上例(PrintArgSimple.java)
构造函数:@OnMethod( clazz="", method=" “)(PrintContructor.java)
192:chapter4 alan$ btrace 3682 PrintConstructor.java
org.alanhou.monitor_tuning.chapter2.User,
[1, Java, ]
拦截同名函数:用参数区分(PrintSame.java)
如下例中虽然方法名相同,但分别有一个和两个参数
@RequestMapping(”/same1")
public String same(@RequestParam(“name”)String name) {
// 访问地址: http://localhost:12345/ch4/same1?name=Java
return "Hello, "+name;
}
@RequestMapping("/same2")
public String same(@RequestParam(“name”)String name, @RequestParam(“id”)int id) {
// 访问地址: http://localhost:12345/ch4/same2?name=Java&id=1
return "Hello, “+name+”, "+id;
}
拦截时机
Kind.ENTRY: 入口,默认值(上述例子均为这种情况)
Kind.RETURN: 返回(PrintReturn.java)
192:chapter4 alan$ btrace 3981 PrintReturn.java
org.alanhou.monitor_tuning.chapter4.Ch4Controller,arg1,Hello, Java
Kind.THROW: 异常(PrintOnThrow.java)
192:chapter4 alan$ btrace 4041 PrintOnThrow.java
java.lang.ClassNotFoundException: org.apache.catalina.webresources.WarResourceSet
…
java.lang.ArithmeticException: / by zero
org.alanhou.monitor_tuning.chapter4.Ch4Controller.exception(Ch4Controller.java:40)
…
Kind.Line: 行(PrintLine.java)
打印指定行号是否执行
192:chapter4 alan$ btrace 4149 PrintLine.java
org.alanhou.monitor_tuning.chapter4.Ch4Controller,exception,39
拦截 this、入参、返回值
this:@self
入参:可以用 AnyType,也可以用真实类型,同名的用真实的
返回:@Return
获取对象的值
简单类型:直接获取
复杂类型:反射,类名+属性名(PrintArgComplex.java)
192:chapter4 alan$ btrace -cp “/Users/alan/Desktop/demo/java-code/monitor_tuning/target/classes” 4337 PrintArgComplex.java
{id=1, name=Java, }
Java
org.alanhou.monitor_tuning.chapter4.Ch4Controller,arg2
拦截函数中还可以使用正则表达式,如method="/.*/"匹配指定类下的所有方法(PrintRegex.java)
打印环境变量(PrintJinfo.java)
Tomcat 性能监控与调优
Tomcat 远程 Debug
JDWP
bin/startup.sh 修改最后一行(添加 jpda)
exec “EXECUTABLE” jpda start “[email protected]”
bin/catalina.sh 为便于远程调试进行如下修改
JPDA_ADDRESS=“localhost:8000”
修改为
JPDA_ADDRESS=“54321”
若发现54321端口启动存在问题可尝试bin/catalina.sh jpda start
本地添加包org.alanhou.monitor_tuning.chapter5,修改打包方式为 war,并重写configure,进入monitor_tuning文件夹,执行mvn clean package 进行打包,target 目录下默认生成的包名为monitor_tuning-0.0.1-SNAPSHOT.war,为便于访问修改为monitor_tuning.war再上传到服务器的webapps目录下
http://192.168.0.5:8080/monitor_tuning/ch5/hello
使用 Eclipse 远程调试,右击 Debug As > Debug Configurations… > Remote Java Application > 右击 New 新建
tomcat-manager 监控
1.conf/tomcat-users.xml添加用户
2.conf/Catalina/localhost/manager.xml配置允许的远程连接
远程连接将allow=“127.0.0.1"修改为allow=”^.*$",浏览器中输入http://127.0.0.1:8080/manage或对应的 IP,用户名密码为tomcat-users.xml中所设置的
3.重启 Tomcat 服务
Tomcat Manager
psi-probe 监控
下载地址:https://github.com/psi-probe/psi-probe,
下载后进入psi-probe-master目录,执行:
mvn clean package -Dmaven.test.skip
将 web/target/probe.war放到 Tomcat 的 webapps 目录下,同样需要conf/tomcat-users.xml和conf/Catalina/localhost/manager.xml中的配置(可保持不变),启动 Tomcat 服务
浏览器中输入http://127.0.0.1:8080/probe或对应的 IP,用户名密码为tomcat-users.xml中所设置的
PSI Probe演示
Tomcat 调优
线程优化(webapps/docs/config/http.html):
maxConnections
acceptCount
maxThreads
minSpareThreads
配置优化(webapps/docs/config/host.html):
autoDeploy
enableLookups(http.html)
reloadable(context.html)
protocol=“org.apache.coyote.http11.Http11AprProtocol”
Session 优化:
如果是 JSP, 可以禁用 Session
补充:APR 配置
yum install -y apr-devel openssl-devel
cd tomcat/bin
tar -zxvf tomcat-native.tar.gz
cd tomcat-native-1.2.17-src/native/
./configure --with-apr=/usr/bin/apr-1-config --with-java-home=/usr/lib/jvm/java-1.8.0 --with-ssl=yes
make && make install
vi tomcat/bin/setenv.sh
export CATALINA_OPTS=”$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib”
#vi tomcat/conf/server.xml
修改为
Nginx 性能监控与调优 Nginx 安装 添加 yum 源(/etc/yum.repos.d/nginx.repo) [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/7/$basesearch/ gpgcheck=0 enabled=1 安装及常用命令 yum install -y nginx systemctl status|start|stop|reload|restart nginx nginx -s stop|reload|quit|reopen cat default.conf | grep -v "#' nginx -V nginx -t 配置反向代理 setenforce 0 ngx_http_stub_status 监控连接信息location = /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
可通过curl http://127.0.0.1/nginx_status 进行查看或注释掉 allow 和 deny 两行使用 IP 进行访问
ngxtop监控请求信息
查看官方使用方法:https://github.com/lebinh/ngxtop
安装 python-pip
yum install epel-release
yum install python-pip
安装 ngxtop
pip install ngxtop
使用示例
指定配置文件:ngxtop -c /etc/nginx/nginx.conf
查询状态是200:ngxtop -c /etc/nginx/nginx.conf -i ‘status == 200’
查询访问最多 ip:ngxtop -c /etc/nginx/nginx.conf -g remote_addr
ngxtop查询访问最多 ip
nginx-rrd 图形化监控
nginx-rrd 依赖于前面的ngx_http_stub_status
安装 php
yum install php php-gd php-soap php-mbstring php-xmlrpc php-dom php-fpm -y
Ngnix融合 php-fpm(/etc/php-fpm.d/www.conf)
user = nginx
group = nginx
启动 php-fpm
systemctl start php-fpm
修改 Nginx 配置文件
location ~ .php$ {
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME fastcgi_script_name;
include fastcgi_params;
}
添加 index.php(/usr/share/nginx/html)
<?php phpinfo(); ?>访问 http://your.ip.address/index.php 检测配置是否成功
安装 rddtool 相关依赖
yum install perl rrdtool perl-libwww-perl libwww-perl perl-rrdtool -y
安装 nginx-rdd
wget http://soft.vpser.net/status/nginx-rrd/nginx-rrd-0.1.4.tgz
tar zxvf nginx-rrd-0.1.4.tgz
cd nginx-rrd-0.1.4
cp usr/sbin/* /usr/sbin # 复制主程序文件到 /usr/sbin 下
cp etc/nginx-rrd.conf /etc # 复制配置文件到 /etc 下
cp html/index.php /usr/share/nginx/html/
修改配置(/etc/nginx-rrd.conf)
RRD_DIR="/usr/share/nginx/html/nginx-rrd";
WWW_DIR="/usr/share/nginx/html";
添加定时任务(crontab -e)
-
-
-
-
- /bin/sh /usr/sbin/nginx-collect
*/1 * * * * /bin/sh /usr/sbin/nginx-graph
- /bin/sh /usr/sbin/nginx-collect
-
-
-
查看定时任务执行情况
tail -f /var/log/cron
ab 压测(未安装 yum -y install httpd-tools)
ab -n 10000 -c 10 http://127.0.0.1/index.html
访问 http://your.ip.address/index.php 即可得到如下这种图形化界面:
nginx-rrd 图形化监控
Nginx 优化
增加工作线程数和并发连接数
worker_processes 4; # 一般CPU 是几核就设置为几
events {
worker_connections 1024; # 每个进程打开的最大连接数,包含了 Nginx 与客户端和 Nginx 与 upstream 之间的连接
multi_accept on; # 可以一次建立多个连接
use epoll;
}
启用长连接
upstream server_pool{
server localhost:8080 weight=1 max_fails=2 fail_timeout=30s;
server localhost:8081 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300; # 300个长连接
}
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_pass http://server_pool;
}
启用缓存压缩
gzip on;
gzip_http_version 1.1;
gzip_disable “MSIE [1-6].(?!.*SV1)”;
gzip_proxied any;
gzip_types text/plain text/css application/javascript application/x-javascript application/json application/xml application/vnd.ms-fontobject application/x-font-ttf application/svg+xml application/x-icon;
gzip_vary on;
gzip_static on;
操作系统优化
配置文件/etc/sysctl.conf
sysctl -w net.ipv4.tcp_syncookies=1 # 防止一个套接字在有过多试图连接到时引起过载
sysctl -w net.core.somaxconn=1024 # 默认128,连接队列
sysctl -w net.ipv4.tcp_fin_timeout=10 # timewait 的超时时间
sysctl -w net.ipv4.tcp_tw_reuse=1 # os 直接使用 timewait的连接
sysctl -w net.ipv4.tcp_tw_recycle=0 # 回收禁用
/etc/security/limits.conf
-
hard nofile 204800
-
soft nofile 204800
-
soft core unlimited
-
soft stack 204800
其它优化
sendfile on; # 减少文件在应用和内核之间拷贝
tcp_nopush on; # 当数据包达到一定大小再发送
tcp_nodelay off; # 有数据随时发送
JVM层GC调优
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html
JVM的内存结构
JVM的内存结构
运行时数据区:
程序计数器 PC Register
虚拟机栈 JVM Stacks
堆 Heap
方法区 Method Area
常量池 Run-Time Constant Pool
本地方法栈 Native Method Stacks
常用参数:
-Xms -Xmx
-XX:NewSize -XX:MaxNewSize
-XX:NewRatio -XX:SurvivorRatio
-XX:MetaspaceSize -XX:MaxMetaspaceSize
-XX:+UseCompressedClassPointers
-XX:CompressedClassSpaceSize
-XX:InitialCodeCacheSize
-XX:ReservedCodeCacheSize
垃圾回收算法
枚举根节点,做可达性分析
根节点:类加载器、Thread、虚拟机栈的本地变量表、static 成员、常量引用、本地方法栈的变量
算法:标记清除、复制、标记整理、分带垃圾回收
对象分配:对象优先在 Eden 区分配、大对象直接进入Old 区(-XX:PretenureSizeThreshold)、长期存活对象进入 Old 区(-XX:MaxTenuringThreshold, -XX:+PrintTenuringDistribution, -XX:TargetSurvivorRatio
垃圾收集器
常见配置示例(bin/catalina.sh)
PARALLEL_OPTION="-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=99"
CMS_OPTION="-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5"
G1_OPTION="-XX:+UseG1GC -XX:+UseStringDeduplication -XX:StringDeduplicationAgeThreshold=3 -XX:+UseCompressedClassPointers -XX:MaxGCPauseMillis=200"
JAVA_OPTS="$JAVA_OPTS $CMS_OPTION -Xms128M -Xmx128M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseCompressedClassPointers"
串行收集器 Serial::Serial, Serial Old (-XX:+UseSerialGC -XX:+UseSerialOldGC)
并行收集器 Parallel(吞吐量优先, Server 模式默认收集器):Parallel Scavenge, Parallel Old (-XX:+UseParallelGC, -XX:+UseParallelOldGC)
-XX:ParallelGCThreads= 多少个 GC 线程(CPU> 8 N=5/8; CPU<8 N=CPU)
Parallel Collector Ergonomics:
-XX:MaxGCPauseMillis=
-XX:GCTimeRatio=
-Xmx
动态内存调整
-XX:YoungGenerationSizeIncrement=
-XX:TenuredGenerationSizeIncrement=
-XX:AdaptiveSizeDecrementScaleFactor=
并发收集器 Concurent(停顿时间优先):CMS (-XX:+UseConcMarkSweepGC -XX:+UseParNewGC), G1(-XX:UseG1GC)
CMS
- CMS initial mark: 初始标记 Root, STW
- CMS concurrent mark:并发标记
- CMS-concurrent-preclean:并发预清理
- CMS remark:重新标记,STW
- CMS concurrent sweep:并发清除
- CMS-concurrent-reset:并发重置
缺点:CPU 敏感、产生垃圾和空间碎片
相关参数:
-XX:ConcGCThreads:并发的 GC 线程数
-XX:+UseCMSCompactAtFullCollection:FullGC 之后做压缩
-XX:CMSFullGCsBeforeCompaction:多少次 FullGC之后压缩一次
-XX:CMSInitiatingOccupancyFraction:触发 FullGC
-XX:+UseCMSInitiatingOccupancyOnly:是否动态可调
-XX:+CMSScavengeBeforeRemark:FullGC之前先做 YGC
-XX:+CMSClassUnloadingEnable:启用回收Perm 区
iCMS
适用于单核或者双核
G1 Collector(JDK 8开始,推荐使用)
G1的几个概念
Region
SATB:Snapshot-At-The-Beginning,它是通过 Root Tracing 得到的,GC 开始时候存活对象的快照。
RSet:记录了其他 Region中的对象引用本 Region 中对象的关系,属于 points-into 结构
YoungGC
新对象进入 Eden 区
存活对象拷贝到Survivor 区
存活时间达到年龄阈值时,对象晋升到 Old 区
MixedGC
不是 FullGC,回收所有的 Young和所有的 Old
global concurrent marking
- Initial marking phase: 标记 GC Root, STW
- Root region scanning phase:标记存活 Region
- Concurrent marking phase:标记存活的对象
- Remark phase:重新标记,STW
- Cleanup phase:部分 STW
MixedGC时机
InitiatingHeapOccupancyPercent
G1HeapWastePercent
G1MixedGCLiveThresholdPercent
G1MixedGCCountTarget
G1OldGCSetRegionThresholdPercent
-XX:+UseG1GC 开启 G1
-XX:G1HeapRegionSize=n, Region 的大小,1-32M,最多2048个
-XX:MaxGCPauseMillis=200 最大停顿时间
-XX:G1NewSizePercent、-XX:G1MaxNewSizePercent
-XX:G1ReservePercent=10 保留防止 to space溢出
-XX:ParallelGCThreads=n SWT线程数
-XX:ConcGCThreads=n 并发线程数=1/4*并行
最佳实践
年轻代大小:避免使用-Xmn, -XX:NewRatio 等显式 Young 区大小,会覆盖暂停时间目标
暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量
需要切换到 G1的情况:
- 50%以上的堆被存活对象占用
- 对象分配和晋升的速度变化非常大
- 垃圾回收时间特别长,超过了1秒
查看方法:jinfo -flag xxx
并行:多条垃圾收集线程并行工作,但用户线程处于等待状态
并发:用户线程与垃圾收集线程同时执行(或交替执行)
停顿时间:垃圾收集器做垃圾回收中断应用执行的时间 -XX:MaxGCPauseMillis
吞吐量:花在垃圾收集的时间和花在应用时间的占比 -XX:GCTimeRatio=,垃圾收集时间占1/(1+n)
垃圾收集器搭配:
垃圾收集器搭配
如何选择垃圾收集器?
- 优先调整堆的大小让服务器自己来选择
- 如果内存小于100M,使用串行收集器
3。 如果是单核,并且没有停顿时间的要求,选择串行或者 JVM 自己选 - 如果允许停顿时间超过1秒,选择并行或 JVM 自己选
- 如果响应时间最重要,并且不能超过1秒,使用并发收集器
可视化 GC 日志分析工具
打印日志相关参数:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log"
CMS日志格式
G1日志格式
在线工具:http://gceasy.io/
访问 GCeasy 官网导入日志即可获取可视化分析及优先建议
GCeasy 日志
GCViewer
mvn clean package -Dmaven.test.skip 生成 jar包,双击执行,导入日志即可进入图形化分析页面
GCViewer
Tomcat 的 GC 调优实战
ParallelGC调优
设置 Metaspace 大小 -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
添加吞吐量和停顿时间参数 -XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=100
修改动态扩容增量 -XX:YoungGenerationSizeIncrement=30
G1 GC 最佳实践
-XX:InitiatingHeapOccupancyPercent: Use to change the marking threshold.
-XX:G1MixedGCLiveThresholdPercent and -XX:G1HeapWastePercent: Use to change the mixed garbage collection decisions.
-XX:G1MixedGCCountTarget and -XX:G1OldCSetRegionThresholdPercent: Use to adjust the CSet for old regions.
JAVA代码层调优
最终代码:monitor_tuning
JVM字节码指令与 javap
javap
cd monitor_tuning/target/classes/org/alanhou/monitor_tuning/chapter8/
javap -verbose Test1.class > Test1.txt 即可保存字节码文件
常量池
字段描述符
方法描述符
字节码指令
i++与++i,字符串拼接+原理
javap -verbose SelfAdd.class > SelfAdd.txt
通过对 f1()和 f2()的字节码,我们得出结论 i++和++i 的执行效果完全相同
public static void f1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: iconst_0
1: istore_0
2: goto 15
5: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_0
9: invokevirtual #31 // Method java/io/PrintStream.println:(I)V
12: iinc 0, 1
15: iload_0
16: bipush 10
18: if_icmplt 5
21: return
其他代码优先方法
字符串拼接+
javap -verbose StringAdd.class >StringAdd.txt
通过字节码可以看出+拼接符效率要低于 append
Try-Finally
javap -verbose TryFinally.class >TryFinally.txt
Constant variable(final)
javap -verbose StringConstant.class >StringConstant.txt
常用代码优化方法
- 尽量重用对象,不要循环创建对象,比如:for 循环字符串拼接(不在 for中使用+拼接,先new 一个StringBuilder再在 for 里 append)
- 容器类初始化的地时候指定长度
List collection = new ArrayLIst(5);
Map<String, String> map = new HashMap<String, String>(32); - ArrayList(底层数组)随机遍历快,LinkedList(底层双向链表)添加删除快
- 集合遍历尽量减少重复计算
- 使用 Entry 遍历 Map
- 大数组复制使用System.arraycopy
- 尽量使用基本类型而不是包装类型
- 不要手动调用 System.gc()
- 及时消除过期对象的引用,防止内存泄漏
- 尽量使用局部变量,减小变量的作用域
- 尽量使用非同步的容器ArraryList vs. Vector
- 尽量减小同步作用范围, synchronized 方法 vs. 代码块
- 用ThreadLocal 缓存线程不安全的对象,SimpleDateFormat
- 尽量使用延迟加载
- 尽量减少使用反射,必须用加缓存
- 尽量使用连接池、线程池、对象池、缓存
- 及时释放资源, I/O 流、Socket、数据库连接
- 慎用异常,不要用抛异常来表示正常的业务逻辑
- String 操作尽量少用正则表达式
- 日志输出注意使用不同的级别
- 日志中参数拼接使用占位符
log.info(“orderId:” + orderId); 不推荐
log.info(“orderId:{}”, orderId); 推荐