一篇年薪60萬的JVM性能調優文章

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

?

JVM 調優概述

性能定義

  • 吞吐量 - 指不考慮 GC 引起的停頓時間或內存消耗,垃圾收集器能支撐應用達到的最高性能指標。
  • 延遲 - 其度量標準是縮短由于垃圾啊收集引起的停頓時間或者完全消除因垃圾收集所引起的停頓,避免應用運行時發生抖動。
  • 內存占用 - 垃圾收集器流暢運行所需要的內存數量。

調優原則

GC 優化的兩個目標:

  1. 將進入老年代的對象數量降到最低
  2. 減少 Full GC 的執行時間

GC 優化的基本原則是:將不同的 GC 參數應用到兩個及以上的服務器上然后比較它們的性能,然后將那些被證明可以提高性能或減少 GC 執行時間的參數應用于最終的工作服務器上。

將進入老年代的對象數量降到最低

除了可以在 JDK7 及更高版本中使用的 G1 收集器以外,其他分代 GC 都是由 Oracle JVM 提供的。關于分代 GC,就是對象在 Eden 區被創建,隨后被轉移到 Survivor 區,在此之后剩余的對象會被轉入老年代。也有一些對象由于占用內存過大,在 Eden 區被創建后會直接被傳入老年代。老年代 GC 相對來說會比新生代 GC 更耗時,因此,減少進入老年代的對象數量可以顯著降低 Full GC 的頻率。你可能會以為減少進入老年代的對象數量意味著把它們留在新生代,事實正好相反,新生代內存的大小是可以調節的。

降低 Full GC 的時間

Full GC 的執行時間比 Minor GC 要長很多,因此,如果在 Full GC 上花費過多的時間(超過 1s),將可能出現超時錯誤。

  • 如果通過減小老年代內存來減少 Full GC 時間,可能會引起 OutOfMemoryError 或者導致 Full GC 的頻率升高。
  • 另外,如果通過增加老年代內存來降低 Full GC 的頻率,Full GC 的時間可能因此增加。

因此,你需要把老年代的大小設置成一個“合適”的值

GC 優化需要考慮的 JVM 參數

類型參數描述
堆內存大小-Xms啟動 JVM 時堆內存的大小
?-Xmx堆內存最大限制
新生代空間大小-XX:NewRatio新生代和老年代的內存比
?-XX:NewSize新生代內存大小
?-XX:SurvivorRatioEden 區和 Survivor 區的內存比

GC 優化時最常用的參數是-Xms,-Xmx-XX:NewRatio-Xms-Xmx參數通常是必須的,所以NewRatio的值將對 GC 性能產生重要的影響。

有些人可能會問如何設置永久代內存大小,你可以用-XX:PermSize-XX:MaxPermSize參數來進行設置,但是要記住,只有當出現OutOfMemoryError錯誤時你才需要去設置永久代內存。

GC 優化的過程

GC 優化的過程和大多數常見的提升性能的過程相似,下面是筆者使用的流程:

1.監控 GC 狀態

你需要監控 GC 從而檢查系統中運行的 GC 的各種狀態。

2.分析監控結果后決定是否需要優化 GC

在檢查 GC 狀態后,你需要分析監控結構并決定是否需要進行 GC 優化。如果分析結果顯示運行 GC 的時間只有 0.1-0.3 秒,那么就不需要把時間浪費在 GC 優化上,但如果運行 GC 的時間達到 1-3 秒,甚至大于 10 秒,那么 GC 優化將是很有必要的。

但是,如果你已經分配了大約 10GB 內存給 Java,并且這些內存無法省下,那么就無法進行 GC 優化了。在進行 GC 優化之前,你需要考慮為什么你需要分配這么大的內存空間,如果你分配了 1GB 或 2GB 大小的內存并且出現了OutOfMemoryError,那你就應該執行**堆快照(heap dump)**來消除導致異常的原因。

注意:

**堆快照(heap dump)**是一個用來檢查 Java 內存中的對象和數據的內存文件。該文件可以通過執行 JDK 中的jmap命令來創建。在創建文件的過程中,所有 Java 程序都將暫停,因此,不要在系統執行過程中創建該文件。

你可以在互聯網上搜索 heap dump 的詳細說明。

3.設置 GC 類型/內存大小

如果你決定要進行 GC 優化,那么你需要選擇一個 GC 類型并且為它設置內存大小。此時如果你有多個服務器,請如上文提到的那樣,在每臺機器上設置不同的 GC 參數并分析它們的區別。

4.分析結果

在設置完 GC 參數后就可以開始收集數據,請在收集至少 24 小時后再進行結果分析。如果你足夠幸運,你可能會找到系統的最佳 GC 參數。如若不然,你還需要分析輸出日志并檢查分配的內存,然后需要通過不斷調整 GC 類型/內存大小來找到系統的最佳參數。

5.如果結果令人滿意,將參數應用到所有服務器上并結束 GC 優化

如果 GC 優化的結果令人滿意,就可以將參數應用到所有服務器上,并停止 GC 優化。

在下面的章節中,你將會看到上述每一步所做的具體工作。

命令

jmap

jmap 即 JVM Memory Map。

jmap 用于生成 heap dump 文件

如果不使用這個命令,還可以使用?-XX:+HeapDumpOnOutOfMemoryError?參數來讓虛擬機出現 OOM 的時候,自動生成 dump 文件。

jmap 不僅能生成 dump 文件,還可以查詢 finalize 執行隊列、Java 堆和永久代的詳細信息,如當前使用率、當前使用的是哪種收集器等。

命令格式:

jmap [option] LVMID

option 參數:

  • dump - 生成堆轉儲快照
  • finalizerinfo - 顯示在 F-Queue 隊列等待 Finalizer 線程執行 finalizer 方法的對象
  • heap - 顯示 Java 堆詳細信息
  • histo - 顯示堆中對象的統計信息
  • permstat - to print permanent generation statistics
  • F - 當-dump 沒有響應時,強制生成 dump 快照

示例:jmap -dump PID 生成堆快照

dump 堆到文件,format 指定輸出格式,live 指明是活著的對象,file 指定文件名

$ jmap -dump:live,format=b,file=dump.hprof 28920
Dumping heap to /home/xxx/dump.hprof ...
Heap dump file created

dump.hprof 這個后綴是為了后續可以直接用 MAT(Memory Anlysis Tool)打開。

示例:jmap -heap 查看指定進程的堆信息

注意:使用 CMS GC 情況下,jmap -heap 的執行有可能會導致 java 進程掛起。

jmap -heap PID
[root@chances bin]# ./jmap -heap 12379
Attaching to process ID 12379, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0-b16using thread-local object allocation.
Parallel GC with 6 thread(s)Heap Configuration:MinHeapFreeRatio = 40MaxHeapFreeRatio = 70MaxHeapSize      = 83886080 (80.0MB)NewSize          = 1310720 (1.25MB)MaxNewSize       = 17592186044415 MBOldSize          = 5439488 (5.1875MB)NewRatio         = 2SurvivorRatio    = 8PermSize         = 20971520 (20.0MB)MaxPermSize      = 88080384 (84.0MB)Heap Usage:
PS Young Generation
Eden Space:capacity = 9306112 (8.875MB)used     = 5375360 (5.1263427734375MB)free     = 3930752 (3.7486572265625MB)57.761608714788736% used
From Space:capacity = 9306112 (8.875MB)used     = 3425240 (3.2665634155273438MB)free     = 5880872 (5.608436584472656MB)36.80634834397007% used
To Space:capacity = 9306112 (8.875MB)used     = 0 (0.0MB)free     = 9306112 (8.875MB)0.0% used
PS Old Generationcapacity = 55967744 (53.375MB)used     = 48354640 (46.11457824707031MB)free     = 7613104 (7.2604217529296875MB)86.39733629427693% used
PS Perm Generationcapacity = 62062592 (59.1875MB)used     = 60243112 (57.452308654785156MB)free     = 1819480 (1.7351913452148438MB)97.06831451706046% used

jstack

jstack 用于生成 java 虛擬機當前時刻的線程快照。

線程快照是當前 java 虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。

線程出現停頓的時候通過 jstack 來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源。 如果 java 程序崩潰生成 core 文件,jstack 工具可以用來獲得 core 文件的 java stack 和 native stack 的信息,從而可以輕松地知道 java 程序是如何崩潰和在程序何處發生問題。另外,jstack 工具還可以附屬到正在運行的 java 程序中,看到當時運行的 java 程序的 java stack 和 native stack 的信息, 如果現在運行的 java 程序呈現 hung 的狀態,jstack 是非常有用的。

命令格式:

jstack [option] LVMID

option 參數:

  • -F?- 當正常輸出請求不被響應時,強制輸出線程堆棧
  • -l?- 除堆棧外,顯示關于鎖的附加信息
  • -m?- 如果調用到本地方法的話,可以顯示 C/C++的堆棧

jps

jps(JVM Process Status Tool),顯示指定系統內所有的 HotSpot 虛擬機進程。

命令格式:

jps [options] [hostid]

option 參數:

  • -l?- 輸出主類全名或 jar 路徑
  • -q?- 只輸出 LVMID
  • -m?- 輸出 JVM 啟動時傳遞給 main()的參數
  • -v?- 輸出 JVM 啟動時顯示指定的 JVM 參數

其中[option]、[hostid]參數也可以不寫。

$ jps -l -m
28920 org.apache.catalina.startup.Bootstrap start
11589 org.apache.catalina.startup.Bootstrap start
25816 sun.tools.jps.Jps -l -m

jstat

jstat(JVM statistics Monitoring),是用于監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT 編譯等運行數據。

命令格式:

jstat [option] LVMID [interval] [count]

參數:

  • [option] - 操作參數
  • LVMID - 本地虛擬機進程 ID
  • [interval] - 連續輸出的時間間隔
  • [count] - 連續輸出的次數

jhat

jhat(JVM Heap Analysis Tool),是與 jmap 搭配使用,用來分析 jmap 生成的 dump,jhat 內置了一個微型的 HTTP/HTML 服務器,生成 dump 的分析結果后,可以在瀏覽器中查看。

注意:一般不會直接在服務器上進行分析,因為 jhat 是一個耗時并且耗費硬件資源的過程,一般把服務器生成的 dump 文件復制到本地或其他機器上進行分析。

命令格式:

jhat [dumpfile]

jinfo

jinfo(JVM Configuration info),用于實時查看和調整虛擬機運行參數。

之前的 jps -v 口令只能查看到顯示指定的參數,如果想要查看未被顯示指定的參數的值就要使用 jinfo 口令

命令格式:

jinfo [option] [args] LVMID

option 參數:

  • -flag : 輸出指定 args 參數的值
  • -flags : 不需要 args 參數,輸出所有 JVM 參數的值
  • -sysprops : 輸出系統屬性,等同于 System.getProperties()

HotSpot VM 參數

詳細參數說明請參考官方文檔:Java HotSpot VM Options,這里僅列舉常用參數。

JVM 內存配置

配置描述
-Xms堆空間初始值。
-Xmx堆空間最大值。
-XX:NewSize新生代空間初始值。
-XX:MaxNewSize新生代空間最大值。
-Xmn新生代空間大小。
-XX:PermSize永久代空間的初始值。
-XX:MaxPermSize永久代空間的最大值。

GC 類型配置

配置描述
-XX:+UseSerialGC串行垃圾回收器
-XX:+UseParallelGC并行垃圾回收器
-XX:+UseParNewGC使用 ParNew + Serial Old 垃圾回收器組合
-XX:+UseConcMarkSweepGC并發標記掃描垃圾回收器
-XX:ParallelCMSThreads=并發標記掃描垃圾回收器 = 為使用的線程數量
-XX:+UseG1GCG1 垃圾回收器

輔助配置

配置描述
-XX:+PrintGCDetails打印 GC 日志
-Xloggc:<filename>指定 GC 日志文件名
-XX:+HeapDumpOnOutOfMemoryError內存溢出時輸出堆快照文件

典型配置

堆大小設置

年輕代的設置很關鍵。

JVM 中最大堆大小有三方面限制:

  1. 相關操作系統的數據模型(32-bt 還是 64-bit)限制;
  2. 系統的可用虛擬內存限制;
  3. 系統的可用物理內存限制。
整個堆大小 = 年輕代大小 + 年老代大小 + 持久代大小
  • 持久代一般固定大小為 64m。使用?-XX:PermSize?設置。
  • 官方推薦年輕代占整個堆的 3/8。使用?-Xmn?設置。

回收器選擇

JVM 給了三種選擇:串行收集器、并行收集器、并發收集器。

JVM 實戰

分析 GC 日志

獲取 GC 日志

獲取 GC 日志有兩種方式:

  • 使用命令動態查看
  • 在容器中設置相關參數打印 GC 日志

jstat -gc?統計垃圾回收堆的行為:

jstat -gc 1262S0C    S1C     S0U     S1U   EC       EU        OC         OU        PC       PU         YGC    YGCT    FGC    FGCT     GCT
26112.0 24064.0 6562.5  0.0   564224.0 76274.5   434176.0   388518.3  524288.0 42724.7    320    6.417   1      0.398    6.815

也可以設置間隔固定時間來打印:

$ jstat -gc 1262 2000 20

這個命令意思就是每隔 2000ms 輸出 1262 的 gc 情況,一共輸出 20 次

Tomcat 設置示例:

JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4
-verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log
-Djava.awt.headless=true
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails
-Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000
-XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15"
  • -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m?Xms,即為 jvm 啟動時得 JVM 初始堆大小,Xmx 為 jvm 的最大堆大小,xmn 為新生代的大小,permsize 為永久代的初始大小,MaxPermSize 為永久代的最大空間。
  • -XX:SurvivorRatio=4?SurvivorRatio 為新生代空間中的 Eden 區和救助空間 Survivor 區的大小比值,默認是 8,則兩個 Survivor 區與一個 Eden 區的比值為 2:8,一個 Survivor 區占整個年輕代的 1/10。調小這個參數將增大 survivor 區,讓對象盡量在 survitor 區呆長一點,減少進入年老代的對象。去掉救助空間的想法是讓大部分不能馬上回收的數據盡快進入年老代,加快年老代的回收頻率,減少年老代暴漲的可能性,這個是通過將-XX:SurvivorRatio 設置成比較大的值(比如 65536)來做到。
  • -verbose:gc -Xloggc:$CATALINA_HOME/logs/gc.log?將虛擬機每次垃圾回收的信息寫到日志文件中,文件名由 file 指定,文件格式是平文件,內容和-verbose:gc 輸出內容相同。
  • -Djava.awt.headless=true?Headless 模式是系統的一種配置模式。在該模式下,系統缺少了顯示設備、鍵盤或鼠標。
  • -XX:+PrintGCTimeStamps -XX:+PrintGCDetails?設置 gc 日志的格式
  • -Dsun.rmi.dgc.server.gcInterval=600000 -Dsun.rmi.dgc.client.gcInterval=600000?指定 rmi 調用時 gc 的時間間隔
  • -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15?采用并發 gc 方式,經過 15 次 minor gc 后進入年老代

如何分析 GC 日志

Young GC 回收日志:

2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]

Full GC 回收日志:

2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

通過上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen 屬于 Parallel 收集器。其中 PSYoungGen 表示 gc 回收前后年輕代的內存變化;ParOldGen 表示 gc 回收前后老年代的內存變化;PSPermGen 表示 gc 回收前后永久區的內存變化。young gc 主要是針對年輕代進行內存回收比較頻繁,耗時短;full gc 會對整個堆內存進行回城,耗時長,因此一般盡量減少 full gc 的次數

通過兩張圖非常明顯看出 gc 日志構成:

687474703a2f2f6974796f756b6e6f772e636f6d2f6173736574732f696d616765732f323031372f6a766d2f596f756e6725323047432e706e67.

687474703a2f2f6974796f756b6e6f772e636f6d2f6173736574732f696d616765732f323031372f6a766d2f46756c6c25323047432e706e67.

OutOfMemory(OOM)分析

OutOfMemory ,即內存溢出,是一個常見的 JVM 問題。那么分析 OOM 的思路是什么呢?

首先,要知道有三種 OutOfMemoryError:

  • OutOfMemoryError:Java heap space?- 堆空間溢出
  • OutOfMemoryError:PermGen space?- 方法區和運行時常量池溢出
  • OutOfMemoryError:unable to create new native thread?- 線程過多

OutOfMemoryError:PermGen space

OutOfMemoryError:PermGen space 表示方法區和運行時常量池溢出。

原因:

Perm 區主要用于存放 Class 和 Meta 信息的,Class 在被 Loader 時就會被放到 PermGen space,這個區域稱為年老代。GC 在主程序運行期間不會對年老區進行清理,默認是 64M 大小。

當程序程序中使用了大量的 jar 或 class,使 java 虛擬機裝載類的空間不夠,超過 64M 就會報這部分內存溢出了,需要加大內存分配,一般 128m 足夠。

解決方案:

(1)擴大永久代空間

  • JDK7 以前使用?-XX:PermSize?和?-XX:MaxPermSize?來控制永久代大小。
  • JDK8 以后把原本放在永久代的字符串常量池移出, 放在 Java 堆中(元空間 Metaspace)中,元數據并不在虛擬機中,使用的是本地的內存。使用?-XX:MetaspaceSize?和?-XX:MaxMetaspaceSize?控制元空間大小。

注意:-XX:PermSize?一般設為 64M

(2)清理應用程序中?WEB-INF/lib?下的 jar,用不上的 jar 刪除掉,多個應用公共的 jar 移動到 Tomcat 的 lib 目錄,減少重復加載。

OutOfMemoryError:Java heap space

OutOfMemoryError:Java heap space 表示堆空間溢出。

原因:JVM 分配給堆內存的空間已經用滿了。

問題定位

(1)使用 jmap 或 -XX:+HeapDumpOnOutOfMemoryError 獲取堆快照。 (2)使用內存分析工具(visualvm、mat、jProfile 等)對堆快照文件進行分析。 (3)根據分析圖,重點是確認內存中的對象是否是必要的,分清究竟是是內存泄漏(Memory Leak)還是內存溢出(Memory Overflow)。

內存泄露

內存泄漏是指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。

內存泄漏并非指內存在物理上的消失,而是應用程序分配某段內存后,由于設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。

內存泄漏隨著被執行的次數越多-最終會導致內存溢出。

而因程序死循環導致的不斷創建對象-只要被執行到就會產生內存溢出。

內存泄漏常見幾個情況:

  • 靜態集合類
    • 聲明為靜態(static)的 HashMap、Vector 等集合
    • 通俗來講 A 中有 B,當前只把 B 設置為空,A 沒有設置為空,回收時 B 無法回收-因被 A 引用。
  • 監聽器
    • 監聽器被注冊后釋放對象時沒有刪除監聽器
  • 物理連接
    • DataSource.getConnection()建立鏈接,必須通過 close()關閉鏈接
  • 內部類和外部模塊等的引用
    • 發現它的方式同內存溢出,可再加個實時觀察
    • jstat -gcutil 7362 2500 70

重點關注:

  • FGC — 從應用程序啟動到采樣時發生 Full GC 的次數。
  • FGCT — 從應用程序啟動到采樣時 Full GC 所用的時間(單位秒)。
  • FGC 次數越多,FGCT 所需時間越多-可非常有可能存在內存泄漏。

解決方案

(1)檢查程序,看是否有死循環或不必要地重復創建大量對象。有則改之。

下面是一個重復創建內存的示例:

public class OOM {public static void main(String[] args) {Integer sum1=300000;Integer sum2=400000;OOM oom = new OOM();System.out.println("往ArrayList中加入30w內容");oom.javaHeapSpace(sum1);oom.memoryTotal();System.out.println("往ArrayList中加入40w內容");oom.javaHeapSpace(sum2);oom.memoryTotal();}public void javaHeapSpace(Integer sum){Random random = new Random();  ArrayList openList = new ArrayList();for(int i=0;i<sum;i++){String charOrNum = String.valueOf(random.nextInt(10));openList.add(charOrNum);}  }public void memoryTotal(){Runtime run = Runtime.getRuntime();long max = run.maxMemory();long total = run.totalMemory();long free = run.freeMemory();long usable = max - total + free;System.out.println("最大內存 = " + max);System.out.println("已分配內存 = " + total);System.out.println("已分配內存中的剩余空間 = " + free);System.out.println("最大可用內存 = " + usable);}
}

執行結果:

往ArrayList中加入30w內容
最大內存 = 20447232
已分配內存 = 20447232
已分配內存中的剩余空間 = 4032576
最大可用內存 = 4032576
往ArrayList中加入40w內容
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:2245)at java.util.Arrays.copyOf(Arrays.java:2219)at java.util.ArrayList.grow(ArrayList.java:242)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)at java.util.ArrayList.add(ArrayList.java:440)at pers.qingqian.study.seven.OOM.javaHeapSpace(OOM.java:36)at pers.qingqian.study.seven.OOM.main(OOM.java:26)

(2)擴大堆內存空間

使用?-Xms?和?-Xmx?來控制堆內存空間大小。

OutOfMemoryError: GC overhead limit exceeded

原因:JDK6 新增錯誤類型,當 GC 為釋放很小空間占用大量時間時拋出;一般是因為堆太小,導致異常的原因,沒有足夠的內存。

解決方案:

查看系統是否有使用大內存的代碼或死循環; 通過添加 JVM 配置,來限制使用內存:

<jvm-arg>-XX:-UseGCOverheadLimit</jvm-arg>

OutOfMemoryError:unable to create new native thread

原因:線程過多

那么能創建多少線程呢?這里有一個公式:

(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads  
MaxProcessMemory 指的是一個進程的最大內存  
JVMMemory         JVM內存  
ReservedOsMemory  保留的操作系統內存  
ThreadStackSize      線程棧的大小

當發起一個線程的創建時,虛擬機會在 JVM 內存創建一個 Thread 對象同時創建一個操作系統線程,而這個系統線程的內存用的不是 JVMMemory,而是系統中剩下的內存: (MaxProcessMemory - JVMMemory - ReservedOsMemory) 結論:你給 JVM 內存越多,那么你能用來創建的系統線程的內存就會越少,越容易發生 java.lang.OutOfMemoryError: unable to create new native thread。

CPU 過高

定位步驟:

(1)執行 top -c 命令,找到 cpu 最高的進程的 id

(2)jstack PID 導出 Java 應用程序的線程堆棧信息。

示例:

jstack 6795"Low Memory Detector" daemon prio=10 tid=0x081465f8 nid=0x7 runnable [0x00000000..0x00000000]  "CompilerThread0" daemon prio=10 tid=0x08143c58 nid=0x6 waiting on condition [0x00000000..0xfb5fd798]  "Signal Dispatcher" daemon prio=10 tid=0x08142f08 nid=0x5 waiting on condition [0x00000000..0x00000000]  "Finalizer" daemon prio=10 tid=0x08137ca0 nid=0x4 in Object.wait() [0xfbeed000..0xfbeeddb8]  at java.lang.Object.wait(Native Method)  - waiting on <0xef600848> (a java.lang.ref.ReferenceQueue$Lock)  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)  - locked <0xef600848> (a java.lang.ref.ReferenceQueue$Lock)  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)  at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)  "Reference Handler" daemon prio=10 tid=0x081370f0 nid=0x3 in Object.wait() [0xfbf4a000..0xfbf4aa38]  at java.lang.Object.wait(Native Method)  - waiting on <0xef600758> (a java.lang.ref.Reference$Lock)  at java.lang.Object.wait(Object.java:474)  at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)  - locked <0xef600758> (a java.lang.ref.Reference$Lock)  "VM Thread" prio=10 tid=0x08134878 nid=0x2 runnable  "VM Periodic Task Thread" prio=10 tid=0x08147768 nid=0x8 waiting on condition

在打印的堆棧日志文件中,tid 和 nid 的含義:

nid : 對應的 Linux 操作系統下的 tid 線程號,也就是前面轉化的 16 進制數字
tid: 這個應該是 jvm 的 jmm 內存規范中的唯一地址定位

在 CPU 過高的情況下,查找響應的線程,一般定位都是用 nid 來定位的。而如果發生死鎖之類的問題,一般用 tid 來定位。

(3)定位 CPU 高的線程打印其 nid

查看線程下具體進程信息的命令如下:

top -H -p 6735

top - 14:20:09 up 611 days,  2:56,  1 user,  load average: 13.19, 7.76, 7.82
Threads: 6991 total,  17 running, 6974 sleeping,   0 stopped,   0 zombie
%Cpu(s): 90.4 us,  2.1 sy,  0.0 ni,  7.0 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
KiB Mem:  32783044 total, 32505008 used,   278036 free,   120304 buffers
KiB Swap:        0 total,        0 used,        0 free.  4497428 cached MemPID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND6800 root      20   0 27.299g 0.021t   7172 S 54.7 70.1 187:55.61 java6803 root      20   0 27.299g 0.021t   7172 S 54.4 70.1 187:52.59 java6798 root      20   0 27.299g 0.021t   7172 S 53.7 70.1 187:55.08 java6801 root      20   0 27.299g 0.021t   7172 S 53.7 70.1 187:55.25 java6797 root      20   0 27.299g 0.021t   7172 S 53.1 70.1 187:52.78 java6804 root      20   0 27.299g 0.021t   7172 S 53.1 70.1 187:55.76 java6802 root      20   0 27.299g 0.021t   7172 S 52.1 70.1 187:54.79 java6799 root      20   0 27.299g 0.021t   7172 S 51.8 70.1 187:53.36 java6807 root      20   0 27.299g 0.021t   7172 S 13.6 70.1  48:58.60 java
11014 root      20   0 27.299g 0.021t   7172 R  8.4 70.1   8:00.32 java
10642 root      20   0 27.299g 0.021t   7172 R  6.5 70.1   6:32.06 java6808 root      20   0 27.299g 0.021t   7172 S  6.1 70.1 159:08.40 java
11315 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   5:54.10 java
12545 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   6:55.48 java
23353 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   2:20.55 java
24868 root      20   0 27.299g 0.021t   7172 S  3.9 70.1   2:12.46 java9146 root      20   0 27.299g 0.021t   7172 S  3.6 70.1   7:42.72 java  

由此可以看出占用 CPU 較高的線程,但是這些還不高,無法直接定位到具體的類。nid 是 16 進制的,所以我們要獲取線程的 16 進制 ID:

printf "%x\n" 6800
輸出結果:45cd

然后根據輸出結果到 jstack 打印的堆棧日志中查定位:

"catalina-exec-5692" daemon prio=10 tid=0x00007f3b05013800 nid=0x45cd waiting on condition [0x00007f3ae08e3000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x00000006a7800598> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:86)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:32)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:745)

?

免費Java資料需要自己領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并發分布式等教程,一共30G。?
傳送門:?https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q

轉載于:https://my.oschina.net/u/3728792/blog/3038275

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279670.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279670.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279670.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

yum 出錯,提示Segmentation Fault (core Dumped) 的解決辦法

CentOS5.5部署Zlib導致yum使用不了&#xff0c;報錯Yum Segmentation Fault (core Dumped) 。 在一臺CentOS.5.5的機器上使用Yum時突然報錯&#xff0c;提示Yum Segmentation Fault (core Dumped) &#xff1b;并產生core.*文件 解決辦法&#xff1a; # rpm -q zlib zlib-devel…

手機主題隨手機殼改變_無線充電可以與手機殼一起使用嗎?

手機主題隨手機殼改變With wireless charging making its way into the new iPhones, there are undoubtedly a lot of questions floating around about how this technology works in practical application. The biggest question I’ve heard so far is: will it work with…

求連續序列的最大子序列和

求一個序列的最大子序列和&#xff0c;這個可以有幾種方法都可以去求解&#xff0c;這里我提供兩種方法給大家。 假如這個序列是{1&#xff0c;-2&#xff0c;3&#xff0c;4}&#xff0c;顯然最大子序列和是7&#xff0c;那么這個要怎么去計算呢&#xff1f; 第一種方法就是順…

Go語言與數據庫開發:01-09

包和工具 Go語言有超過100個的標準包&#xff08;譯注&#xff1a;可以用 go list std | wc -l 命令查看標準包的具體數目&#xff09;&#xff0c;標準庫為大多數的程序提供了必要的基礎構件。在Go的社區&#xff0c;有很多成熟的包被設計、共享、重用和改進&#xff0c;目前互…

android 文本后圖標_如何在Android中更改文本,圖標等的大小

android 文本后圖標Let’s face it: no matter how good the screens are on our phones and tablets, the text can sometimes be too tiny if you have poor eyesight. The good news is that there are a variety of methods to help you alleviate squinting just to make …

Code Chef February Challenge 2019題解

傳送門 \(HMAPPY2\) 咕 話說這題居然卡\(scanf\)的么&#xff1f;&#xff1f;&#xff1f; int T;cin>>T; while(T--){cin>>n>>a>>b>>k;puts(n/an/b-n/(a*b/__gcd(a,b))*2>k?"Win":"Lose"); } \(CHEFING\) 咕咕 int T;…

Linux文本查看命令之uniq

uniq是專用的去重命令限制&#xff1a;必須相鄰的兩行內容相同才算是重復&#xff0c;如果內容相同&#xff0c;但是兩行之間有其他內容就不算重復。使用uniq命令先排序&#xff0c;再去重。-d 的選項是用來僅顯示重復行的-u 僅顯示不重復的行-c 統計每一行出現的次數本文轉自 …

BitMap位圖與海量數據的理解與應用

1. Bit Map算法簡介 來自于《編程珠璣》。所謂的Bit-map就是用一個bit位來標記某個元素對應的Value&#xff0c; 而Key即是該元素。由于采用了Bit為單位來存儲數據&#xff0c;因此在存儲空間方面&#xff0c;可以大大節省。 2、 Bit Map的基本思想 我們先來看一個具體的例子&a…

imdb文件_如何停止IMDB應用程序向您發送通知

imdb文件Recently, the IMDB app started sending out notifications for “Featured Trailers”. As near as I can guess, this is where the production company pays IMDB to push a link to the trailer to a load of people in an effort to promote it. If IMDB isn’t …

科普:BCH能夠買什么?如何使用BCH買東西?

2019獨角獸企業重金招聘Python工程師標準>>> 一提到BCH&#xff0c;你最想拿它做什么&#xff1f;可能對于投資者來說&#xff0c;它是暴富的神器&#xff0c;是投資的工具&#xff1b;對于開發者來說&#xff0c;是實現自身價值構建應用程序的網絡和平臺&#xff0…

驅動學習之驅動體驗

1&#xff1a;什么是linux驅動 從本質上講&#xff0c;驅動就是屬于內核層面的程序代碼&#xff0c;是直接和硬件打交道的。與裸機中直接操作寄存器去操作硬件的不同之處在于&#xff0c;裸機中操作的是物理內存&#xff0c;而我們在驅動中操作的是虛擬內存&#xff0c;驅動中還…

vim(三)golang代碼跳轉配

在golang的代碼里跳來跳去。。。。 godef 安裝 跳轉是通過godef實現&#xff0c;godef的安裝目錄一般是$GOBIN,只要讓godef命令在$PATH下即可 godef 命令安裝&#xff1a; go get -v github.com/rogpeppe/godef go install -v github.com/rogpeppe/godef vim插件安裝 ~/.vimrc配…

如何將iPhone或iPad更新到iOS 11

Apple released iOS 11 on September 19, 2017. You can upgrade by tapping “Install Now” when an update message appears, but you can also check for the update and install it immediately. 蘋果于2017年9月19日發布了iOS11 。您可以通過在出現更新消息時點按“立即安…

三、Python-列表

三、Python-列表 一、序列&#xff1a;是一塊用于存放多個值的連續內存空間&#xff0c;并且按一定順序排列&#xff0c;可以通過索引取值索引&#xff1a;從左到右的索引從0開始依次增加的正整數&#xff1b;從右到左的索引為-1開始的復數切片&#xff08;分片&#xff09;&am…

使用基本ACL規則限制用戶登錄

要求&#xff1a;配置ACL 2005規則&#xff0c;限制vty 0 4界面只允許IP地址為192.168.1.8的用戶和10.10.100.0/24網段的用戶登錄設備。 配置如下&#xff1a; system-view acl 2005 rule permit source 192.168.1.8 0 //允許IP地址為192.168.1.8的用戶登錄設備 rule permit s…

pandas 入門(2)

from pandas import Series, DataFrame, Index import numpy as np from numpy import nan as NAobj Series(range(3), index[a, b, c]) print(obj) index obj.index print(index) print(index[1:]) # index[1] d index對象時不可以被修改的 為了安全和共享index Index(n…

如何在Outlook 2013中管理附件

There comes a time, job-hunting, or sharing photos with older family members, where you may need to send stuff the old fashioned way – as an email attachment. If you email at work, it may be a part of your email repertoire. 有時需要找工作&#xff0c;與年長…

了解cron以及使用cron定時備份MySQL

cron是一個linux下的定時執行工具&#xff0c;可以在無需人工干預的情況下運行作業。由于Cron 是Linux的內置服務&#xff0c;但它不自動起來&#xff0c;可以用以下的方法啟動、關閉這個服務&#xff1a; /sbin/service crond start //啟動服務 /sbin/service crond stop //關…

ef 并發控制

ef 并發控制 ef 并發控制 什么是并發&#xff1f;并發分悲觀并發和樂觀并發。悲觀并發&#xff1a;比如有兩個用戶A,B&#xff0c;同時登錄系統修改一個文檔&#xff0c;如果A先進入修改&#xff0c;則系統會把該文檔鎖住&#xff0c;B就沒辦法打開了&#xff0c;只有等A修改完…

C#實現寫入文本文件內容功能

private void write_txt(string str1, string str2, string str3)02{03System.DateTime currentTime System.DateTime.Now;04string strYMD currentTime.ToString("d");05string FILE_NAME "MyFileSend" strYMD ".txt";//每天按照日期建立一…