回覆列表
  • 1 # 俠夢的開發筆記
    寫在前面

    java程式的效能問題定位,一直都是開發者需要面對的一個“攔路虎”, 在前面的兩篇文章中,已經介紹了Heap dump的概念和生成方式,以及Shallow heap和Retained heap以及GC ROOT的概念,本篇文章,我們繼續來介紹一些新的概念和基於一個dump案例,詳盡的介紹,在程式OOM後,改如何去定位具體原因。

    再次提及dominator tree(支配樹)

    如果你玩過競技類遊戲,肯定會很熟悉Dominating -主宰比賽這個單詞。

    在Memory Analyzer工具中提供了物件圖的支配樹。將物件引用圖轉換為dominator tree可以讓輕鬆地確定Retained heap最大記憶體塊和物件之間保持活動的依賴關係,相當於主宰了整個JVM的感覺。

    我們把java物件之間的引用關係看做一張有向圖,如果所有指向Y的物件路徑都要經過X,則我們說X支配Y。如果X是離物件Y最近的支配物件,則我們說物件X是Y的直接支配者( immediate dominator)。

    再強化記憶一下,X物件要直接支配Y,必須滿足這兩點:

    指向Y的物件路徑都要經過X。X物件是離Y最近的支配物件。

    左邊是物件的引用圖,右邊是支配樹。

    C節點的子樹就是所有被C支配的節點的集合,也稱為C的Retained Set。

    由圖可以看出,C是E的直接支配節點,所以C的上級支配節點B也可以支配E。

    dump分析初步

    首先用MemoryAnalyzer工具開啟dump檔案。

    從整體情況可以看出,1.6 gb的堆記憶體,有大物件佔了1.1g。

    懷疑是有記憶體洩漏,我們透過Leak Suspect Report報告檢視

    記憶體洩漏分析報告顯示有兩項問題:

    一是WebappClassLoader 類載入器裝載的A.A[][] 物件佔了約1.2g(70.40%)。二是一個名為TP-Processor9的執行緒持有本地變數多達337M(佔了19.58%)。

    透過分析報告,我們初步可以推斷出OOM的問題應該出在這兩個地方,我們逐個擊破。

    記憶體洩漏點一

    先來看類裝載器載入的AA物件。我們點開記憶體洩漏報告的Detail,檢視其詳情。

    Shortest Paths To the Accumulation Point檢視可以看出正是和org.apache.catalina.loader.WebappClassLoader這個GC root相連導致當前Retained Heap佔用相當大的物件無法被回收,而物件數量居然達到了170288個。

    Accumulated Objects in Dominator Tree檢視,可以看出AA物件中,到底是什麼內嵌物件佔用heap高。

    可以看出,170288個AA物件陣列內部,主要是AH物件 和 AM物件。

    我們繼續向下看,透過按class類分組,來看看具體佔用比例情況。

    按class分組後,冒出了一個Af物件,反倒AH佔用不是那麼多了。

    小結:AH/AM/Af三個物件佔用堆記憶體很高,並且它們的gc root是WebappClassLoader。

    記憶體洩漏點二

    TP-Processor9 執行緒本身就是GC root,故只有一條資料。

    以這個執行緒為GC ROOT來看看,它的支配樹是什麼樣的?

    以TP-Processor9 執行緒為gc root的支配樹,按class分類,可以發現Am物件本身佔用163m左右。

    Detail明細的最後由於當前懷疑洩露點為TP-Processor9 執行緒物件,故展示了執行緒明細資訊,呼叫棧資訊。

    小結:TP-Processor9 執行緒內部可能導致記憶體洩漏。

    整合兩個洩漏點結論

    從兩個洩漏點,得出的結論,我們可以將問題定位到如下元素上: TP-Processor9 執行緒、170288個AA物件陣列、AM、AH物件。

    我們來看看AA物件陣列的(Outgoing Reference)引用其他外部物件的情況

    可以看到com.fr.report.core.A.A[170288][] @ 0xcfdb62a0 這個物件引用外部物件的情況,一共是170289個 com.fr.report.core.A.A[25] 物件。

    並且其Shallow Heap 和Retained Heap大小一樣。 170289 * 120 = 20434680。 約佔20M記憶體。

    所以這個物件應用的外部物件,不是根因。

    接下來,再分析Incomming Reference(被其他外部物件引用的情況)。

    可以看到com.fr.report.core.A.A[170288][] @ 0xcfdb62a0

    這個包含170288個元素的物件,主要被 com.fr.report.worksheet.PageRWorkSheet @ 0xcfd02188 物件持有。

    出現了一系列的ReportPage和ClippedECPage物件。

    從依賴樹和物件地址可以知道,ClippedECPage引用ReportPage物件。

    所以只需要重點關注ClippedECPage物件。繼續分析ClippedECPage物件的依賴樹如下:

    其他的 Shallow Heap佔用都不高,唯獨一個物件陣列,佔用較大,並且它的Retained Heap很大。

    問題應該就出在ClippedECPage類上,進一步分析它儲存的 com.fr.page.ReportPage。 這個物件是: 用於展示及列印的頁面 執行完一個多Sheet的Report後, 會生成多個ReportPage。 分析它的屬性:

    連續查看了兩個物件,發現其中的屬性,currentPageNumber為591和592。

    總頁數為599. 可以推斷出,是在做分頁操作。可以看到當時瀏覽器的資訊

    操作的檔案:xxx/customized/xxx表.xxx

    結論

    結合ReportPage類的作用: 用於展示及列印的頁面 執行完一個多Sheet的Report後, 會生成多個ReportPage。

    最後結論為:

    在執行查詢時間段為: 2018/6/8 15:57:15 - 2018/6/8 16:0:53時 一次性列印599頁資料,導致OOM,程式宕機崩潰。

    至此,就完成了一個Dump檔案的分析,我準備了一個mat的操作手冊,比較全面。

  • 2 # kid7157887

    jmap dump堆記憶體,注意在生產環境使用cms gc dump會導致stop the world。dump完成後使用mat或jprofile分析。如果緊急,可以用jmap -histo匯出堆使用情況,簡單分析下。同時可以用jstack打出執行緒棧,用gceasy或者fastthread線上進行分析。

  • 3 # 淺紫彼岸花開

    一般在java中,記憶體是由jvm管理,當記憶體佔用過多,jvm會把不用的資源給釋放,所以一般程式設計師不需要手動釋放。

  • 4 # 有骨有度

    記憶體溢位一般也不容易遇見,而且Java有個好玩的東東是,好多問題,升級到最新穩定的JDK問題就自然解決了。

    參考這篇:一次生產 CPU 100% 排查最佳化實踐(https://mp.weixin.qq.com/s/c8_y5jq1SqPTe2YsgOhyZQ)

    1. 找到存在問題的程序,ps aux,jps之類的命令都可以做到;

    2. 根據找到的程序 ps -Hp打印出當前執行緒,看佔用資源比較多的執行緒堆疊;

    3. jstack列印執行緒堆疊資訊,需要慢慢看,畢竟看起來都是正常執行著的。

    記憶體溢位其實一般來說是階段性的,這時就需要記錄gc日誌和業務日誌了,根據日誌來找出問題的蛛絲馬跡。一般好多東西都是程式設計錯誤,或者錯誤的使用了大物件等。

  • 中秋節和大豐收的關聯?
  • 單反相機要配什麼規格鏡頭才能拍出漂亮的人像“糖水片”?佳能機身有什麼推薦的鏡頭?