本文主要內容包含
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
-XX:+UseG1GC
-
非Boolean類型
格式:-XX:<name>=<value>表示 name 屬性的值是 value
如:-XX:MaxGCPauseMillis=500
-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
補充:測試中需要用到 Tomcat,CentOS 7安裝示例如下
sudo yum -y install java-1.8.0-openjdk*
wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-8/v8.5.32/bin/apache-tomcat-8.5.32.tar.gz
tar -zxvf apache-tomcat-8.5.32.tar.gz
mv apache-tomcat-8.5.32 tomcat
cd tomcat/bin/
sh startup.sh
pid 可通過類似 ps -ef|grep tomcat或 jps來進行查看
jps
詳情參考 jps官方文檔
-l
jinfo
jinfo -flag MaxHeapSize <pid>
jinfo -flags <pid>
jstat
詳情參考 jstat 官方文檔
類加載
# 以下1000表每隔1000ms 即1秒,共輸出10次
jstat -class <pid> 1000 10
垃圾收集
-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
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:<dump-options>, -F
jmap -dump:format=b,file=heap.hprof <pid>
MAT下載地址:http://www.eclipse.org/mat/
找開上述導出的內存溢出文件即可進行分析,如下圖的溢出源頭分析:
jstack
詳情參考 jstack 官方文檔
jstack <pid>
可查看其中包含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 <pid> -H可以查看線程及 CPU 消耗情況
使用 jstack <pid>可以導出追蹤文件,文件中 PID 在 jstack 中顯示的對應 nid 爲十六進制(命令行可執行 print '%x' <pid>可以進行轉化,如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 <pid>導出追蹤記錄會發現如下這樣的記錄)
Java stack information for the threads listed above:
===================================================
"Thread-5":
at org.alanhou.monitor_tuning.chapter2.CpuController.lambda$deadlock$1(CpuController.java:41)
- waiting to lock <0x00000000edcf3470> (a java.lang.Object)
- locked <0x00000000edcf3480> (a java.lang.Object)
at org.alanhou.monitor_tuning.chapter2.CpuController$$Lambda$337/547045985.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-4":
at org.alanhou.monitor_tuning.chapter2.CpuController.lambda$deadlock$0(CpuController.java:33)
- waiting to lock <0x00000000edcf3480> (a java.lang.Object)
- locked <0x00000000edcf3470> (a java.lang.Object)
at org.alanhou.monitor_tuning.chapter2.CpuController$$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
以上是本地的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
以上爲 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 <pid> <trace_script>
monitor_tuning中新增包org.alanhou.monitor_tuning.chapter4
安裝BTrace 要記得配置環境變量,以 Mac 爲例
# vi ~/.bash_profile
BTRACE_HOME=/Applications/btrace
PATH=$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="<init> ")(PrintContructor.java)
192:chapter4 alan$ btrace 3682 PrintConstructor.java
org.alanhou.monitor_tuning.chapter2.User,<init>
[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 "$PRGDIR"/"$EXECUTABLE" jpda start "$@"
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添加用戶
<role rolename="tomcat"/>
<role rolename="manager-status"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="123456" roles="tomcat,manager-gui,manager-status"/>
2.conf/Catalina/localhost/manager.xml配置允許的遠程連接
<?xml version="1.0" encoding="UTF-8"?>
<Context privileged="true" antiResourceLocking="false"
docBase="$(catalina.home)/webapps/manager">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1" />
</Context>
遠程連接將allow="127\.0\.0\.1"修改爲allow="^.*$",瀏覽器中輸入http://127.0.0.1:8080/manage或對應的 IP,用戶名密碼爲tomcat-users.xml中所設置的
3.重啓 Tomcat 服務
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中所設置的
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
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
# 修改爲
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
connectionTimeout="20000"
redirectPort="8443" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
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
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 $document_root/$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
# 查看定時任務執行情況
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 優化
增加工作線程數和併發連接數
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的內存結構
運行時數據區:
程序計數器 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=<N> 多少個 GC 線程(CPU> 8 N=5/8; CPU<8 N=CPU)
Parallel Collector Ergonomics:
-XX:MaxGCPauseMillis=<N>
-XX:GCTimeRatio=<N>
-Xmx<N>
動態內存調整
-XX:YoungGenerationSizeIncrement=<Y>
-XX:TenuredGenerationSizeIncrement=<T>
-XX:AdaptiveSizeDecrementScaleFactor=<D>
併發收集器 Concurent(停頓時間優先):CMS (-XX:+UseConcMarkSweepGC -XX:+UseParNewGC), G1(-XX:UseG1GC)
CMS
1. CMS initial mark: 初始標記 Root, STW
2. CMS concurrent mark:併發標記
3. CMS-concurrent-preclean:併發預清理
4. CMS remark:重新標記,STW
5. CMS concurrent sweep:併發清除
6. 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
1. Initial marking phase: 標記 GC Root, STW
2. Root region scanning phase:標記存活 Region
3. Concurrent marking phase:標記存活的對象
4. Remark phase:重新標記,STW
5. 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的情況:
1. 50%以上的堆被存活對象佔用
2. 對象分配和晉升的速度變化非常大
3. 垃圾回收時間特別長,超過了1秒
查看方法:jinfo -flag xxx <pid>
並行:多條垃圾收集線程並行工作,但用戶線程處於等待狀態
併發:用戶線程與垃圾收集線程同時執行(或交替執行)
停頓時間:垃圾收集器做垃圾回收中斷應用執行的時間 -XX:MaxGCPauseMillis
吞吐量:花在垃圾收集的時間和花在應用時間的佔比 -XX:GCTimeRatio=<n>,垃圾收集時間佔1/(1+n)
垃圾收集器搭配:
如何選擇垃圾收集器?
1. 優先調整堆的大小讓服務器自己來選擇
2. 如果內存小於100M,使用串行收集器
3。 如果是單核,並且沒有停頓時間的要求,選擇串行或者 JVM 自己選
4. 如果允許停頓時間超過1秒,選擇並行或 JVM 自己選
5. 如果響應時間最重要,並且不能超過1秒,使用併發收集器
可視化 GC 日誌分析工具
打印日誌相關參數:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
例(默認爲 ParallelGC, 其它的添加-XX:+UseConcMarkSweepGC或-XX:+UseG1GC即可):
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log"
在線工具:http://gceasy.io/
訪問 GCeasy 官網導入日誌即可獲取可視化分析及優先建議
mvn clean package -Dmaven.test.skip 生成 jar包,雙擊執行,導入日誌即可進入圖形化分析頁面
Tomcat 的 GC 調優實戰
ParallelGC調優
設置 Metaspace 大小 -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
添加吞吐量和停頓時間參數 -XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=100
修改動態擴容增量 -XX:YoungGenerationSizeIncrement=30
-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 <options> <classes>
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
常用代碼優化方法
1. 儘量重用對象,不要循環創建對象,比如:for 循環字符串拼接(不在 for中使用+拼接,先new 一個StringBuilder再在 for 裏 append)
2. 容器類初始化的地時候指定長度
List<String> collection = new ArrayLIst<String>(5);
Map<String, String> map = new HashMap<String, String>(32);
3. ArrayList(底層數組)隨機遍歷快,LinkedList(底層雙向鏈表)添加刪除快
4. 集合遍歷儘量減少重複計算
5. 使用 Entry 遍歷 Map
6. 大數組複製使用System.arraycopy
7. 儘量使用基本類型而不是包裝類型
8. 不要手動調用 System.gc()
9. 及時消除過期對象的引用,防止內存泄漏
10. 儘量使用局部變量,減小變量的作用域
11. 儘量使用非同步的容器ArraryList vs. Vector
12. 儘量減小同步作用範圍, synchronized 方法 vs. 代碼塊
13. 用ThreadLocal 緩存線程不安全的對象,SimpleDateFormat
14. 儘量使用延遲加載
15. 儘量減少使用反射,必須用加緩存
16. 儘量使用連接池、線程池、對象池、緩存
17. 及時釋放資源, I/O 流、Socket、數據庫連接
18. 慎用異常,不要用拋異常來表示正常的業務邏輯
19. String 操作儘量少用正則表達式
20. 日誌輸出注意使用不同的級別
21. 日誌中參數拼接使用佔位符
log.info("orderId:" + orderId); 不推薦
log.info("orderId:{}", orderId); 推薦
課程鏈接:https://coding.imooc.com/class/241.html