国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

記一次 Java 應用內(nèi)存泄漏的定位過程

開發(fā) 后端
最近,筆者負責測試的某個算法模塊機器出現(xiàn)大量報警,報警表現(xiàn)為機器CPU持續(xù)高占用。

 [[418418]]

問題現(xiàn)象

最近,筆者負責測試的某個算法模塊機器出現(xiàn)大量報警,報警表現(xiàn)為機器CPU持續(xù)高占用。該算法模塊是一個優(yōu)化算法,本身就是CPU密集型應用,一開始懷疑可能是算法在正常運算,但很快這種猜測就被推翻:同算法同學確認后,該算法應用只使用了一個核心,而報警時,一個算法進程占用了服務(wù)機器的全部8個核心,這顯然不是正常計算造成的。

定位步驟

首先按照CPU問題的定位思路進行定位,對 Java 調(diào)用堆棧進行分析:

    1.  使用top -c 查看 CPU 占用高的進程:   

,從 top 命令的結(jié)果看,19272 號進程 CPU 占用率最高,基本確定問題是該進程引起,可以從 Command 欄看到這正是算法模塊程序,注意圖是線下4C機器上復現(xiàn)時的截圖。

    2.  使用ps -mp pid -o THREAD,tid,time命令定位問題線程。 

  1. ps -mp 19272 -o THREAD,tid,time  
  2.   USER     %CPU PRI SCNT WCHAN  USER SYSTEM   TID     TIME  
  3.   USER    191   -    - -         -      -     - 00:36:54  
  4.   USER    0.0  19    - futex_    -      - 19272 00:00:00  
  5.   USER   68.8  19    - futex_    -      - 19273 00:13:18  
  6.   USER   30.2  19    - -         -      - 19274 00:05:50  
  7.   USER   30.2  19    - -         -      - 19275 00:05:50  
  8.   USER   30.2  19    - -         -      - 19276 00:05:50  
  9.   USER   30.1  19    - -         -      - 19277 00:05:49  
  10.   USER    0.4  19    - futex_    -      - 19278 00:00:05  
  11.   USER    0.0  19    - futex_    -      - 19279 00:00:00  
  12.   USER    0.0  19    - futex_    -      - 19280 00:00:00  
  13.   USER    0.0  19    - futex_    -      - 19281 00:00:00  
  14.   USER    0.4  19    - futex_    -      - 19282 00:00:04  
  15.   USER    0.3  19    - futex_    -      - 19283 00:00:03  
  16.   USER    0.0  19    - futex_    -      - 19284 00:00:00  
  17.   USER    0.0  19    - futex_    -      - 19285 00:00:00  
  18.   USER    0.0  19    - futex_    -      - 19286 00:00:00  
  19.   USER    0.0  19    - skb_wa    -      - 19362 00:00:00 

從結(jié)果可以看到,出現(xiàn)問題的線程主要是 19273-19277。

    3.  使用jstack查看出現(xiàn)問題的線程堆棧信息。

由于 jstack 使用的線程號是十六進制,因此需要先把線程號從十進制轉(zhuǎn)換為十六進制。 

  1. $ printf "%x\n" 19273  
  2. 4b49 
  3. $ jstack 12262 |grep -A 15 4b49  
  4. "main" #1 prio=5 os_prio=0 tid=0x00007f98c404c000 nid=0x4b49 runnable [0x00007f98cbc58000]  
  5. java.lang.Thread.State: RUNNABLE  
  6.     at java.util.ArrayList.iterator(ArrayList.java:840)  
  7.     at optional.score.MultiSkuDcAssignmentEasyScoreCalculator.updateSolution(MultiSkuDcAssignmentEasyScoreCalculator.java:794)  
  8.     at optional.score.MultiSkuDcAssignmentEasyScoreCalculator.calculateScore(MultiSkuDcAssignmentEasyScoreCalculator.java:80)  
  9.     at optional.score.MultiSkuDcAssignmentEasyScoreCalculator.calculateScore(MultiSkuDcAssignmentEasyScoreCalculator.java:17)  
  10.     at org.optaplanner.core.impl.score.director.easy.EasyScoreDirector.calculateScore(EasyScoreDirector.java:60)  
  11.     at org.optaplanner.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:188)  
  12.     at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:132)  
  13.     at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:116)  
  14.     at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70)  
  15.     at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:88)  
  16.     at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:191)  
  17.     at app.DistributionCenterAssignmentApp.main(DistributionCenterAssignmentApp.java:61)  
  18. "VM Thread" os_prio=0 tid=0x00007f98c419d000 nid=0x4b4e runnable 
  19. "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f98c405e800 nid=0x4b4a runnable  
  20. "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f98c4060800 nid=0x4b4b runnable  
  21. "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f98c4062800 nid=0x4b4c runnable  
  22. "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f98c4064000 nid=0x4b4d runnable  
  23. "VM Periodic Task Thread" os_prio=0 tid=0x00007f98c4240800 nid=0x4b56 waiting on condition 

可以看到,除了 0x4b49 線程是正常工作線程,其它都是 gc 線程。

此時懷疑:是頻繁 GC 導致的 CPU 被占滿。

我們可以使用 jstat 命令查看 GC 統(tǒng)計: 

  1. $ jstat -gcutil 19272 2000 10  
  2. S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT  
  3. 0.00   0.00  22.71 100.00  97.16  91.53   2122   19.406   282  809.282  828.688  
  4. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   283  809.282  828.688  
  5. 0.00   0.00  92.46 100.00  97.16  91.53   2122   19.406   283  812.730  832.135  
  6. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   284  812.730  832.135  
  7. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   285  815.965  835.371  
  8. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   285  815.965  835.371  
  9. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   286  819.492  838.898  
  10. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   286  819.492  838.898  
  11. 0.00   0.00 100.00 100.00  97.16  91.53   2122   19.406   287  822.751  842.157  
  12. 0.00   0.00  30.78 100.00  97.16  91.53   2122   19.406   287  825.835  845.240 

重點關(guān)注一下幾列:

YGC:年輕代垃圾回收次數(shù)

YGCT:年輕代垃圾回收消耗時間

FGC:老年代垃圾回收次數(shù)

FGCT:老年代垃圾回收消耗時間

GCT:垃圾回收消耗總時間

可以看到,20s 的時間中進行了 5 次 full GC,僅僅耗費在 GC 的時間已經(jīng)到了 17s。

    1.  增加啟動參數(shù),展示詳細 GC 過程。通過增加 jvm 參數(shù),更快暴露 GC 問題,并展示 GC 詳細過程java -Xmx1024m -verbose:gc。 

  1. [Full GC (Ergonomics)  1046527K->705881K(1047552K), 1.8974837 secs]  
  2. [Full GC (Ergonomics)  1046527K->706191K(1047552K), 2.5837756 secs]  
  3. [Full GC (Ergonomics)  1046527K->706506K(1047552K), 2.6142270 secs]  
  4. [Full GC (Ergonomics)  1046527K->706821K(1047552K), 1.9044987 secs]  
  5. [Full GC (Ergonomics)  1046527K->707130K(1047552K), 2.0856625 secs]  
  6. [Full GC (Ergonomics)  1046527K->707440K(1047552K), 2.6273944 secs]  
  7. [Full GC (Ergonomics)  1046527K->707755K(1047552K), 2.5668877 secs]  
  8. [Full GC (Ergonomics)  1046527K->708068K(1047552K), 2.6924427 secs]  
  9. [Full GC (Ergonomics)  1046527K->708384K(1047552K), 3.1084132 secs]  
  10. [Full GC (Ergonomics)  1046527K->708693K(1047552K), 1.9424100 secs]  
  11. [Full GC (Ergonomics)  1046527K->709007K(1047552K), 1.9996261 secs]  
  12. [Full GC (Ergonomics)  1046527K->709314K(1047552K), 2.4190958 secs]  
  13. [Full GC (Ergonomics)  1046527K->709628K(1047552K), 2.8139132 secs]  
  14. [Full GC (Ergonomics)  1046527K->709945K(1047552K), 3.0484079 secs]  
  15. [Full GC (Ergonomics)  1046527K->710258K(1047552K), 2.6983539 secs] 
  16. [Full GC (Ergonomics)  1046527K->710571K(1047552K), 2.1663274 secs] 

至此基本可以確定,CPU 高負載的根本原因是內(nèi)存不足導致頻繁 GC。

根本原因

雖然我們經(jīng)過上面的分析可以知道,是頻繁 GC 導致的 CPU 占滿,但是并沒有找到問題的根本原因,因此也無從談起如何解決。GC 的直接原因是內(nèi)存不足,懷疑算法程序存在內(nèi)存泄漏。

為什么會內(nèi)存泄漏

雖然 Java 語言天生就有垃圾回收機制,但是這并不意味著 Java 就沒有內(nèi)存泄漏問題。

正常情況下,在 Java 語言中如果一個對象不再被使用,那么 Java 的垃圾回收機制會及時把這些對象所占用的內(nèi)存清理掉。但是有些情況下,有些對象雖然不再被程序使用,但是仍然有引用指向這些對象,所以垃圾回收機制無法處理。隨著這些對象占用內(nèi)存數(shù)量的增長,最終會導致內(nèi)存溢出。

Java 的內(nèi)存泄漏問題比較難以定位,下面針對一些常見的內(nèi)存泄漏場景做介紹:

    1.  持續(xù)在堆上創(chuàng)建對象而不釋放。例如,持續(xù)不斷的往一個列表中添加對象,而不對列表清空。這種問題,通常可以給程序運行時添加 JVM 參數(shù)-Xmx 指定一個較小的運行堆大小,這樣可以比較容易的發(fā)現(xiàn)這類問題。

    2. 不正 所有的靜態(tài)變量。

    3.  對大 String 對象調(diào)用 String.intern()方法,該方法會從字符串常量池中查詢當前字符串是否存在,若不存在就會將當前字符串放入常量池中。而在 jdk6 之前,字符串常量存儲在 PermGen 區(qū)的,但是默認情況下 PermGen 區(qū)比較小,所以較大的字符串調(diào)用此方法,很容易會觸發(fā)內(nèi)存溢出問題。

    4.  打開的輸入流、連接沒有爭取關(guān)閉。由于這些資源需要對應的內(nèi)存維護狀態(tài),因此不關(guān)閉會導致這些內(nèi)存無法釋放。

如何進行定位

以上介紹了一些常見的內(nèi)存泄漏場景,在實際的問題中還需要針對具體的代碼進行確定排查。下面結(jié)合之前的頻繁 GC 問題,講解一下定位的思路,以及相關(guān)工具的使用方法。

線上定位

對于線上服務(wù),如果不能開啟 Debug 模式,那么可用的工具較少。推薦方式:

使用 top -c 命令查詢 Java 高內(nèi)存占用程序的進程 pid。然后使用 jcmd 命令獲取進程中對象的計數(shù)、內(nèi)存占用信息。 

  1. $ jcmd 24600 GC.class_histogram |head -n 10  
  2. 24600:  
  3.  num     #instances         #bytes  class name  
  4. ----------------------------------------------  
  5.    1:       2865351      103154208  [J  
  6.    2:       1432655       45844960  org.optaplanner.core.impl.localsearch.scope.LocalSearchMoveScope  
  7.    3:       1432658       34383792  org.optaplanner.core.api.score.buildin.bendablelong.BendableLongScore  
  8.    4:       1193860       28652640  org.optaplanner.core.impl.heuristic.selector.move.generic.ChangeMove  
  9.    5:        241961       11986056  [Ljava.lang.Object;  
  10.    6:        239984        5759616  java.util.ArrayList 

結(jié)果中,#instances 為對象數(shù)量,#bytes 為占用內(nèi)存大小,單位是 byte,class name 為對應的類名。

排名第一的是 Java 原生類型,實際上是 long 類型。

另外,要注意的是結(jié)果中的類可能存在包含關(guān)系,例如一個類中含有多個 long 類型數(shù)據(jù),那 long 對應的計數(shù)也會增加,所以我們要排除一些基本類型,它們可能是我們程序中使用導致的計數(shù)增加,重點關(guān)注我們程序中的類。

如果僅僅有 jcmd 的結(jié)果,其實很難直接找到問題的根本原因。如果問題不能在線下復現(xiàn),我們基本上只能針對計數(shù)較多的類名跟蹤變量的數(shù)據(jù)流,重點關(guān)注 new 對象附近的代碼邏輯。觀察代碼邏輯時,重點考慮上述幾種常見內(nèi)存泄漏場景。

線下定位

如果內(nèi)存泄漏問題可以在線下復現(xiàn),那么問題定位的工具就比較豐富了。下面主要推薦的兩種工具,VisualVM & IDEA。

這里主要講一下IDEA調(diào)試定位思路:

使用 IDEA 調(diào)試器定位內(nèi)存泄漏問題

如果以上過程依然不能有效的分析出問題的根本原因,還可以使用 IDEA 的調(diào)試功能進行定位。

配置好程序的運行參數(shù),正常復現(xiàn)問題之后,對程序打斷點并逐步追蹤。

重點關(guān)注的是程序需要大量運行時間的代碼部分,我們可以使用調(diào)試暫停功能獲得一個內(nèi)存快照。

然后在此運行并暫停,這時候在調(diào)試的 Memory 視圖中可以看到哪些類在快速增加。基本上可以斷定問題的原因是兩次運行中 new 該對象的語句。

定位結(jié)果

經(jīng)過上述定位步驟,最終發(fā)現(xiàn)問題的根本原因,在求解器的 LocalSearch 階段,如果使用了禁忌搜索(Tabu Search)策略,并且長時間找不到更好的解,會不斷把當前經(jīng)歷過的解加到禁忌表中。對應的代碼部分,finalListScore 是一個 list,在 55 行代碼不斷的添加 moveScope 對象,導致了內(nèi)存泄漏:

解決方案

在求解器該處代碼對 finalListScore 進行長度限制,添加對象前發(fā)現(xiàn)達到了上限就清空,徹底避免內(nèi)存泄漏的發(fā)生。由于出問題的是一個開源求解器框架:optaplanner,為了方便以后維護,按照開源項目貢獻流程,把改fix提PR給項目即可,如何給開源項目提PR可以參考社區(qū)文章:https://testerhome.com/topics/2114

細節(jié)參考PR鏈接:https://github.com/kiegroup/optaplanner/pull/726

項目維護者從代碼維護的角度沒有接受這個PR,但是使用了類似的fix思路最終修復了這個存在了幾年bug:https://github.com/kiegroup/optaplanner/pull/763

最后,算法模塊升級到最新版本的optaplanner依賴即可修復該問題。 

責任編輯:龐桂玉 來源: JAVA高級架構(gòu)
相關(guān)推薦

2022-02-08 17:17:27

內(nèi)存泄漏排查

2022-09-13 17:46:19

STA模式內(nèi)存

2021-11-02 07:54:41

內(nèi)存.NET 系統(tǒng)

2018-09-14 10:48:45

Java內(nèi)存泄漏

2021-02-11 14:06:38

Linux內(nèi)核內(nèi)存

2019-08-26 09:50:09

2017-12-19 14:00:16

數(shù)據(jù)庫MySQL死鎖排查

2021-12-02 07:50:30

NFS故障內(nèi)存

2023-01-04 18:32:31

線上服務(wù)代碼

2023-10-10 12:05:45

2022-01-10 09:31:17

Jetty異步處理seriesbaid

2020-11-02 09:48:35

C++泄漏代碼

2017-11-09 16:07:00

Web應用內(nèi)存

2021-11-23 21:21:07

線上排查服務(wù)

2021-02-01 09:00:34

Ceph octopu集群運維

2011-08-08 13:31:44

數(shù)據(jù)分析數(shù)據(jù)倉庫

2017-09-22 10:16:16

MySQL數(shù)據(jù)庫用戶數(shù)據(jù)

2019-02-20 09:29:44

Java內(nèi)存郵件

2022-10-09 10:47:37

NET視覺軟件

2020-08-27 21:36:50

JVM內(nèi)存泄漏
點贊
收藏

51CTO技術(shù)棧公眾號

国产精品99久久久久久似苏梦涵| 韩国av一区| 九色成人免费视频| 亚洲一区二区av| 中文字幕日韩电影| 精品国产乱子伦一区二区| 91亚洲va在线va天堂va国 | 国产精品一区二区久久| 免费日本视频一区| 久草精品在线播放| 欧美主播一区二区三区美女| 国精产品一区二区三区有限公司 | 精品69视频一区二区三区Q| 成人小视频在线观看免费| 亚洲国产另类精品专区| 刘亦菲一区二区三区免费看| 91福利视频导航| 久久久国产午夜精品| 免费看男男www网站入口在线| 日韩在线欧美在线| 久久久一二三| 精品美女视频在线观看免费软件| 日韩有码在线播放| 视频在线观看国产精品| 簧片在线观看| 欧美大片欧美激情性色a∨久久| 久久一区精品| 免费网站成人| 国产精品黄页免费高清在线观看| 久久69国产一区二区蜜臀| 精东影业在线观看| 一区二区国产盗摄色噜噜| 男人天堂亚洲天堂| 57pao国产成人免费| 成人激情小说网站| 9色在线观看| 91青青草免费观看| 粉嫩久久99精品久久久久久夜| 猫咪在线永久网站| 国产精品免费看久久久香蕉| 成人h精品动漫一区二区三区| 性开放的欧美大片| 精品无人区一区二区三区竹菊 | 日本免费高清视频| 美乳少妇欧美精品| 久久久www成人免费无遮挡大片| 欧美大电影免费观看| 国产一区一区三区| 亚洲午夜精品在线| 国产欧美一区二区三区米奇| 日本在线xxx| www.亚洲男人天堂| 久久久国际精品| 婷婷综合福利| 最新在线地址| 不卡一区二区三区视频| 欧美麻豆精品久久久久久| 亚洲一区二区三区免费在线观看| 99se视频在线观看| 亚洲人成77777| 中日韩美女免费视频网站在线观看| 国产v综合v亚洲欧| 久久免费资源| xxx亚洲日本| 国产亚洲一区二区三区在线播放| 精品国免费一区二区三区| 成人黄色一级视频| 久久丁香四色| 在线免费国产| 中文字幕中文字幕在线中心一区| 日韩在线观看网站| 精品福利免费观看| 性欧美videos另类喷潮| 国产激情精品一区二区三区| wwwav91| 日韩欧美精品一区二区| 精品中文字幕乱| 亚洲欧美日韩电影| 蜜桃91丨九色丨蝌蚪91桃色| 日韩偷拍自拍| 成人欧美一区二区三区视频| 精品久久久久香蕉网| 亚洲欧美日韩国产中文在线| 免费久久99精品国产| 色橹橹欧美在线观看视频高清| 超碰在线最新网址| 在线观看黄色小视频| 国产又粗又大又爽的视频| 国产日韩精品电影| 欧美黄色小视频| 国产亚洲精品久久久优势| 狠狠爱在线视频一区| 欧美国产1区2区| 亚洲色图图片| 中文在线中文字幕| 8x海外华人永久免费日韩内陆视频| 久久精品国产久精国产爱| 黄网站在线免费| 欧美整片在线观看| 国产日产欧美精品一区二区三区| 伊人av免费在线观看| 欧美精品做受xxx性少妇| re久久精品视频| av免费不卡国产观看| 九色蝌蚪在线| 亚洲尤物在线视频| 精东影业在线观看| 粗大的内捧猛烈进出在线视频| 91精品无人成人www| 精品视频无码一区二区三区| 国产日韩欧美精品在线观看| 蜜桃av久久久亚洲精品| 久久大片网站| 蜜桃视频成人在线观看| 91精品久久久久久久久久久久久久| 国产91精品露脸国语对白| 希岛爱理一区二区三区| 日日碰狠狠添天天爽超碰97| 欧美日韩激情网| 欧美国产日韩精品免费观看| 久久婷婷一区二区三区| 国产亚洲一区字幕| 国产精品久久久久久久久晋中| 国产午夜精品久久久久久久| 中文字幕在线不卡一区二区三区| 国产精品美女久久久久久久网站| 亚洲摸摸操操av| 欧美日韩精品三区| 亚洲免费av片| 国内偷自视频区视频综合| 国产日韩中文字幕| 欧美专区一二三| 漂亮人妻被中出中文字幕| 色网在线视频| 国产自产自拍视频在线观看| 久久久久九九精品影院| 欧美日韩亚洲一区| 99久免费精品视频在线观看| 天天综合天天综合色| 亚洲第一精品福利| 久久久免费观看视频| 国产精品视频免费观看| 99久久免费观看| 日韩a在线看| 最新日韩一区| 韩日成人av| caoporn国产一区二区| 欧美一级精品| 久久99国产精品久久| 久久婷婷成人综合色| 欧美日韩一卡二卡三卡| 欧美激情性做爰免费视频| 久久久久无码国产精品一区| 东北一级毛片| 向日葵视频成人app网址| 激情亚洲网站| 精品色蜜蜜精品视频在线观看| 中文字幕精品久久| 日韩影院一区| 日本黄色片在线观看| 欧美一区二区三区四区高清| 国产网站欧美日韩免费精品在线观看| 欧美一区二区视频97| 亚洲狠狠婷婷综合久久久| 精品在线播放午夜| 国产成人福利片| 欧美一区亚洲| 欧美久久成人| 亚洲一区二区欧美激情| 丝袜亚洲另类欧美重口| 国产在线视频综合| 在线观看福利电影| 久久电影网站中文字幕| 欧美精品一区二区三区蜜桃视频| 91偷拍精品一区二区三区| 轻轻色免费在线视频| 色综合久久一区二区三区| 亚洲一二三四区| 成人中文字幕在线观看| 精东影业在线观看| 久久久久国产精品| 欧美午夜片在线看| 日韩欧美精品在线不卡 | 久久久99精品免费观看| 中文字幕亚洲一区二区三区五十路 | 7878视频在线观看| 要久久电视剧全集免费| 亚洲国产综合在线| 高清国产在线一区| 牛牛电影国产一区二区| 国产麻豆视频一区| 欧美国产日韩在线| 中文字幕免费在线观看| 亚洲麻豆av| 中文国产成人精品| 亚洲欧美视频二区| 91精品高清| 亚洲天堂av在线免费观看| 亚洲国产精品久久久久爰色欲| 色婷婷色综合|