https://gitee.com/vectorx/NOT...
[TOC]
<mark>支付寶:</mark>
支付寶三面:JVM效能調優都做了什麼?
<mark>小米:</mark>
有做過JVM記憶體優化嗎?
從SQL、JVM、架構、資料庫四個方面講講優化思路
<mark>螞蟻金服:</mark>
JVM的編譯優化
jvm效能調優都做了什麼
JVM診斷調優工具用過哪些?
二面:jvm怎樣調優,堆記憶體、棧空間設定多少合適
三面:JVM相關的分析工具使用過的有哪些?具體的效能調優步驟如何
<mark>阿里:</mark>
如何進行JVM調優?有哪些方法?
如何理解記憶體漏失問題?有哪些情況會導致記憶體漏失?如何解決?
<mark>位元組跳動:</mark>
三面:JVM如何調優、引數怎麼調?
<mark>拼多多:</mark>
從SQL、JVM、架構、資料庫四個方面講講優化思路
<mark>京東:</mark>
JVM診斷調優工具用過哪些?
每秒幾十萬並行的秒殺系統為什麼會頻繁發生GC?
日均百萬級交易系統如何優化JVM?
線上生產系統OOM如何監控及定位與解決?
高並行系統如何基於G1垃圾回收器優化效能?
生產環境中的問題
為什麼要調優
不同階段的考慮
監控的依據
調優的大方向
第1步:效能監控
第2步:效能分析
第3步:效能調優
停頓時間(或響應時間)
提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間。常用操作的響應時間列表:
操作 | 響應時間 |
---|---|
開啟一個站點 | 幾秒 |
資料庫查詢一條記錄(有索引) | 十幾毫秒 |
機械磁碟一次定址定位 | 4毫秒 |
從機械磁碟順序讀取1M資料 | 2毫秒 |
從SSD磁碟順序讀取1M資料 | 0.3毫秒 |
從遠端分散式換成Redis 讀取一個資料 | 0.5毫秒 |
從記憶體讀取 1M資料 | 十幾微妙 |
Java程式本地方法呼叫 | 幾微妙 |
網路傳輸2Kb資料 | 1 微妙 |
在垃圾回收環節中:
吞吐量
並行數
記憶體佔用
相互間的關係
以高速公路通行狀況為例
<hr/>
效能診斷是軟體工程師在日常工作中需要經常面對和解決的問題,在使用者體驗至上的今天,解決好應用的效能問題能帶來非常大的收益。
Java 作為最流行的程式語言之一,其應用效能診斷一直受到業界廣泛關注。可能造成 Java 應用出現效能問題的因素非常多,例如執行緒控制、磁碟讀寫、資料庫存取、網路I/O、垃圾收集等。想要定位這些問題,一款優秀的效能診斷工具必不可少。
體會1:使用資料說明問題,使用知識分析問題,使用工具處理問題。
體會2:無監控、不調優!
簡單命令列工具
在我們剛接觸java學習的時候,大家肯定最先了解的兩個命令就是javac,java,那麼除此之外,還有沒有其他的命令可以供我們使用呢?
我們進入到安裝jdk的bin目錄,發現還有一系列輔助工具。這些輔助工具用來獲取目標 JVM 不同方面、不同層次的資訊,幫助開發人員很好地解決Java應用程式的一些疑難雜症。
官方原始碼地址:http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/jdk.jcmd/share/classes/sun/tools
jps(Java Process Status):顯示指定系統內所有的HotSpot虛擬機器器程序(檢視虛擬機器器程序資訊),可用於查詢正在執行的虛擬機器器程序。
說明:對於本地虛擬機器器程序來說,程序的本地虛擬機器器ID與作業系統的程序ID是一致的,是唯一的。
基本使用語法為:jps [options] [hostid]
我們還可以通過追加引數,來列印額外的資訊。
options引數
說明:以上引數可以綜合使用。
補充:如果某 Java 程序關閉了預設開啟的UsePerfData引數(即使用引數-XX:-UsePerfData),那麼jps命令(以及下面介紹的jstat)將無法探知該Java 程序。
hostid引數
RMI登入檔中註冊的主機名。如果想要遠端監控主機上的 java 程式,需要安裝 jstatd。
對於具有更嚴格的安全實踐的網路場所而言,可能使用一個自定義的策略檔案來顯示對特定的可信主機或網路的存取,儘管這種技術容易受到IP地址欺詐攻擊。
如果安全問題無法使用一個客製化的策略檔案來處理,那麼最安全的操作是不執行jstatd伺服器,而是在本地使用jstat和jps工具。
jstat(JVM Statistics Monitoring Tool):用於監視虛擬機器器各種執行狀態資訊的命令列工具。它可以顯示本地或者遠端虛擬機器器程序中的類裝載、記憶體、垃圾收集、JIT編譯等執行資料。在沒有GUI圖形介面,只提供了純文字控制檯環境的伺服器上,它將是執行期定位虛擬機器器效能問題的首選工具。常用於檢測垃圾回收問題以及記憶體漏失問題。
官方檔案:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
基本使用語法為:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
檢視命令相關引數:jstat-h 或 jstat-help
其中vmid是程序id號,也就是jps之後看到的前面的號碼,如下:
option引數
選項option可以由以下值構成。
<mark>類裝載相關的:</mark>
<mark>垃圾回收相關的:</mark>
<mark>JIT相關的:</mark>
jstat -class
jstat -compiler
jstat -printcompilation
jstat -gc
jstat -gccapacity
jstat -gcutil
jstat -gccause
jstat -gcnew
jstat -gcnewcapacity
jstat -gcold
jstat -gcoldcapacity
jstat -t
jstat -t -h
表頭 | 含義(位元組) |
---|---|
EC | Eden區的大小 |
EU | Eden區已使用的大小 |
S0C | 倖存者0區的大小 |
S1C | 倖存者1區的大小 |
S0U | 倖存者0區已使用的大小 |
S1U | 倖存者1區已使用的大小 |
MC | 元空間的大小 |
MU | 元空間已使用的大小 |
OC | 老年代的大小 |
OU | 老年代已使用的大小 |
CCSC | 壓縮類空間的大小 |
CCSU | 壓縮類空間已使用的大小 |
YGC | 從應用程式啟動到取樣時young gc的次數 |
YGCT | 從應用程式啟動到取樣時young gc消耗時間(秒) |
FGC | 從應用程式啟動到取樣時full gc的次數 |
FGCT | 從應用程式啟動到取樣時的full gc的消耗時間(秒) |
GCT | 從應用程式啟動到取樣時gc的總時間 |
interval引數: 用於指定輸出統計資料的週期,單位為毫秒。即:查詢間隔
count引數: 用於指定查詢的總次數
-t引數: 可以在輸出資訊前加上一個Timestamp列,顯示程式的執行時間。單位:秒
-h引數: 可以在週期性資料輸出時,輸出多少行資料後輸出一個表頭資訊
補充: jstat還可以用來判斷是否出現記憶體漏失。
第1步:在長時間執行的 Java 程式中,我們可以執行jstat命令連續獲取多行效能資料,並取這幾行資料中 OU 列(即已佔用的老年代記憶體)的最小值。
第2步:然後,我們每隔一段較長的時間重複一次上述操作,來獲得多組 OU 最小值。如果這些值呈上漲趨勢,則說明該 Java 程式的老年代記憶體已使用量在不斷上漲,這意味著無法回收的物件在不斷增加,因此很有可能存在記憶體漏失。
jinfo(Configuration Info for Java):檢視虛擬機器器設定引數資訊,也可用於調整虛擬機器器的設定引數。在很多情況卡,Java應用程式不會指定所有的Java虛擬機器器引數。而此時,開發人員可能不知道某一個具體的Java虛擬機器器引數的預設值。在這種情況下,可能需要通過查詢檔案獲取某個引數的預設值。這個查詢過程可能是非常艱難的。但有了jinfo工具,開發人員可以很方便地找到Java虛擬機器器引數的當前值。
基本使用語法為:jinfo [options] pid
說明:java 程序ID必須要加上
選項 | 選項說明 |
---|---|
no option | 輸出全部的引數和系統屬性 |
-flag name | 輸出對應名稱的引數 |
-flag [+-]name | 開啟或者關閉對應名稱的引數 只有被標記為manageable的引數才可以被動態修改 |
-flag name=value | 設定對應名稱的引數 |
-flags | 輸出全部的引數 |
-sysprops | 輸出系統屬性 |
jinfo -sysprops
> jinfo -sysprops
jboss.modules.system.pkgs = com.intellij.rt
java.vendor = Oracle Corporation
sun.java.launcher = SUN_STANDARD
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
catalina.useNaming = true
os.name = Windows 10
...
jinfo -flags
> jinfo -flags 25592
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=333447168 -XX:MaxHeapSize=5324668928 -XX:MaxNewSize=1774714880 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=111149056 -XX:OldSize=222298112 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:8040,suspend=y,server=n -Drebel.base=C:\Users\Vector\.jrebel -Drebel.env.ide.plugin.version=2021.1.2 -Drebel.env.ide.version=2020.3.3 -Drebel.env.ide.product=IU -Drebel.env.ide=intellij -Drebel.notification.url=http://localhost:7976 -agentpath:C:\Users\Vector\AppData\Roaming\JetBrains\IntelliJIdea2020.3\plugins\jr-ide-idea\lib\jrebel6\lib\jrebel64.dll -Dmaven.home=D:\eclipse\env\maven -Didea.modules.paths.file=C:\Users\Vector\AppData\Local\JetBrains\IntelliJIdea2020.3\Maven\idea-projects-state-596682c7.properties -Dclassworlds.conf=C:\Users\Vector\AppData\Local\Temp\idea-6755-mvn.conf -Dmaven.ext.class.path=D:\IDEA\plugins\maven\lib\maven-event-listener.jar -javaagent:D:\IDEA\plugins\java\lib\rt\debugger-agent.jar -Dfile.encoding=UTF-8
jinfo -flag
> jinfo -flag UseParallelGC 25592
-XX:+UseParallelGC
> jinfo -flag UseG1GC 25592
-XX:-UseG1GC
jinfo -flag name
> jinfo -flag UseParallelGC 25592
-XX:+UseParallelGC
> jinfo -flag UseG1GC 25592
-XX:-UseG1GC
jinfo -flag [+-]name
> jinfo -flag +PrintGCDetails 25592
> jinfo -flag PrintGCDetails 25592
-XX:+PrintGCDetails
> jinfo -flag -PrintGCDetails 25592
> jinfo -flag PrintGCDetails 25592
-XX:-PrintGCDetails
拓展:
java -XX:+PrintFlagsInitial 檢視所有JVM引數啟動的初始值
[Global flags]
intx ActiveProcessorCount = -1 {product}
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
uintx AdaptiveSizePausePolicy = 0 {product}
...
java -XX:+PrintFlagsFinal 檢視所有JVM引數的最終值
[Global flags]
intx ActiveProcessorCount = -1 {product}
...
intx CICompilerCount := 4 {product}
uintx InitialHeapSize := 333447168 {product}
uintx MaxHeapSize := 1029701632 {product}
uintx MaxNewSize := 1774714880 {product}
java -XX:+PrintCommandLineFlags 檢視哪些已經被使用者或者JVM設定過的詳細的XX引數的名稱和值
-XX:InitialHeapSize=332790016 -XX:MaxHeapSize=5324640256 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
jmap(JVM Memory Map):作用一方面是獲取dump檔案(堆轉儲快照檔案,二進位制檔案),它還可以獲取目標Java程序的記憶體相關資訊,包括Java堆各區域的使用情況、堆中物件的統計資訊、類載入資訊等。開發人員可以在控制檯中輸入命令「jmap -help」查閱jmap工具的具體使用方式和一些標準選項設定。
官方幫助檔案:https://docs.oracle.com/en/java/javase/11/tools/jmap.html
基本使用語法為:
選項 | 作用 |
---|---|
-dump | 生成dump檔案(Java堆轉儲快照),-dump:live只儲存堆中的存活物件 |
-heap | 輸出整個堆空間的詳細資訊,包括GC的使用、堆設定資訊,以及記憶體的使用資訊等 |
-histo | 輸出堆空間中物件的統計資訊,包括類、範例數量和合計容量,-histo:live只統計堆中的存活物件 |
-J <flag> | 傳遞引數給jmap啟動的jvm |
-finalizerinfo | 顯示在F-Queue中等待Finalizer執行緒執行finalize方法的物件,僅linux/solaris平臺有效 |
-permstat | 以ClassLoader為統計口徑輸出永久代的記憶體狀態資訊,僅linux/solaris平臺有效 |
-F | 當虛擬機器器程序對-dump選項沒有任何響應時,強制執行生成dump檔案,僅linux/solaris平臺有效 |
說明:這些引數和linux下輸入顯示的命令多少會有不同,包括也受jdk版本的影響。
> jmap -dump:format=b,file=<filename.hprof> <pid>> jmap -dump:live,format=b,file=<filename.hprof> <pid>
由於jmap將存取堆中的所有物件,為了保證在此過程中不被應用執行緒干擾,jmap需要藉助安全點機制,讓所有執行緒停留在不改變堆中資料的狀態。也就是說,由jmap匯出的堆快照必定是安全點位置的。這可能導致基於該堆快照的分析結果存在偏差。
舉個例子,假設在編譯生成的機器碼中,某些物件的生命週期在兩個安全點之間,那麼:live選項將無法探知到這些物件。
另外,如果某個執行緒長時間無法跑到安全點,jmap將一直等下去。與前面講的jstat則不同,垃圾回收器會主動將jstat所需要的摘要資料儲存至固定位置之中,而jstat只需直接讀取即可。
jhat(JVM Heap Analysis Tool):Sun JDK提供的jhat命令與jmap命令搭配使用,用於分析jmap生成的heap dump檔案(堆轉儲快照)。jhat內建了一個微型的HTTP/HTML伺服器,生成dump檔案的分析結果後,使用者可以在瀏覽器中檢視分析結果(分析虛擬機器器轉儲快照資訊)。
使用了jhat命令,就啟動了一個http服務,埠是7000,即http://localhost:7000/,就可...
說明:jhat命令在JDK9、JDK10中已經被刪除,官方建議用VisualVM代替。
基本適用語法:jhat <option> <dumpfile>
option引數 | 作用 |
---|---|
-stack false|true | 關閉|開啟物件分配呼叫棧跟蹤 |
-refs false|true | 關閉|開啟物件參照跟蹤 |
-port port-number | 設定jhat HTTP Server的埠號,預設7000 |
-exclude exclude-file | 執行物件查詢時需要排除的資料成員 |
-baseline exclude-file | 指定一個基準堆轉儲 |
-debug int | 設定debug級別 |
-version | 啟動後顯示版本資訊就退出 |
-J <flag> | 傳入啟動引數,比如-J-Xmx512m |
jstack(JVM Stack Trace):用於生成虛擬機器器指定程序當前時刻的執行緒快照(虛擬機器器堆疊跟蹤)。執行緒快照就是當前虛擬機器器內指定程序的每一條執行緒正在執行的方法堆疊的集合。
生成執行緒快照的作用:可用於定位執行緒出現長時間停頓的原因,如執行緒間死鎖、死迴圈、請求外部資源導致的長時間等待等問題。這些都是導致執行緒長時間停頓的常見原因。當執行緒出現停頓時,就可以用jstack顯示各個執行緒呼叫的堆疊情況。
官方幫助檔案:https://docs.oracle.com/en/java/javase/11/tools/jstack.html
在thread dump中,要留意下面幾種狀態
option引數 | 作用 |
---|---|
-F | 當正常輸出的請求不被響應時,強制輸出執行緒堆疊 |
-l | 除堆疊外,顯示關於鎖的附加資訊 |
-m | 如果呼叫本地方法的話,可以顯示C/C++的堆疊 |
在JDK 1.7以後,新增了一個命令列工具jcmd。它是一個多功能的工具,可以用來實現前面除了jstat之外所有命令的功能。比如:用它來匯出堆、記憶體使用、檢視Java程序、匯出執行緒資訊、執行GC、JVM執行時間等。
官方幫助檔案:https://docs.oracle.com/en/java/javase/11/tools/jcmd.html
jcmd擁有jmap的大部分功能,並且在Oracle的官方網站上也推薦使用jcmd命令代jmap命令
jcmd -l:列出所有的JVM程序
jcmd 程序號 help:針對指定的程序,列出支援的所有具體命令
jcmd 程序號 具體命令:顯示指定程序的指令命令的資料
之前的指令只涉及到監控本機的Java應用程式,而在這些工具中,一些監控工具也支援對遠端計算機的監控(如jps、jstat)。為了啟用遠端監控,則需要配合使用jstatd 工具。命令jstatd是一個RMI伺服器端程式,它的作用相當於代理伺服器,建立本地計算機與遠端監控工具的通訊。jstatd伺服器將本機的Java應用程式資訊傳遞到遠端計算機。