国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一文深度講解JVM 內(nèi)存分析工具 MAT及實踐(建議收藏)

這篇具有很好參考價值的文章主要介紹了一文深度講解JVM 內(nèi)存分析工具 MAT及實踐(建議收藏)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1. 前言

熟練掌握 MAT 是 Java 高手的必備能力,但實踐時大家往往需面對眾多功能,眼花繚亂不知如何下手,小編也沒有找到一篇完善的教學素材,所以整理本文幫大家系統(tǒng)掌握 MAT 分析工具。

本文詳細講解 MAT 眾多內(nèi)存分析工具功能,這些功能組合使用異常強大,熟練使用幾乎可以解決所有的堆內(nèi)存離線分析的問題。我們將功能劃分為4類:內(nèi)存分布詳情、對象間依賴、對象狀態(tài)詳情、按條件檢索。每大類有多個功能點,本文會逐一講解各功能的場景及用法。此外,添加了原創(chuàng)或引用案例加強理解和掌握。

如圖所示:

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

為減少對眼花繚亂的菜單的迷茫,可以通過下圖先整體熟悉下各功能使用入口,后續(xù)都會講到。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

2. 內(nèi)存分布詳解及實戰(zhàn)

2.1 全局信息概覽

功能:展現(xiàn)堆內(nèi)存大小、對象數(shù)量、class 數(shù)量、class loader 數(shù)量、GC Root 數(shù)量、環(huán)境變量、線程概況等全局統(tǒng)計信息。

使用入口:MAT 主界面 → Heap Dump Overview。

舉例:下面是對象數(shù)量、class loader 數(shù)量、GC Root 數(shù)量,可以看出 class loader 存在異常。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

舉例:下圖是線程概況,可以查看每個線程名、線程的 Retained Heap、daemon 屬性等。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用場景 全局概覽呈現(xiàn)全局統(tǒng)計信息,重點查看整體是否有異常數(shù)據(jù),所以有效信息有限,下面幾種場景有一定幫助:

  • 方法區(qū)溢出時(Java 8后不使用方法區(qū),對應堆溢出),查看 class 數(shù)量異常多,可以考慮是否為動態(tài)代理類異常載入過多或類被反復重復加載。

  • 方法區(qū)溢出時,查看 class loader 數(shù)量過多,可以考慮是否為自定義 class loader 被異常循環(huán)使用。

  • GC Root 過多,可以查看 GC Root 分布,理論上這種情況極少會遇到,筆者只在 JNI 使用一個存在 BUG 的庫時遇到過。

  • 線程數(shù)過多,一般是頻繁創(chuàng)建線程但無法執(zhí)行結束,從概覽可以了解異常表象,具體原因可以參考本文線程分析部分內(nèi)容,此處不展開。

2.2 Dominator tree

注:筆者使用頻率的 Top1,是高效分析 Dump 必看的功能。

功能

  • 展現(xiàn)對象的支配關系圖,并給出對象支配內(nèi)存的大小(支配內(nèi)存等同于 Retained Heap,即其被 GC 回收可釋放的內(nèi)存大?。?/p>

  • 支持排序、支持按 package、class loader、super class、class 聚類統(tǒng)計

使用入口:全局支配樹: MAT 主界面 → Dominator tree。

舉例: 下圖中通過查看 Dominator tree,了解到內(nèi)存主要是由 ThreadAndListHolder-thread 及 main 兩個線程支配(后面第2.6節(jié)會給出整體案例)。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用場景

  • 開始 Dump 分析時,首先應使用 Dominator tree 了解各支配樹起點對象所支配內(nèi)存的大小,進而了解哪幾個起點對象是 GC 無法釋放大內(nèi)存的原因。

  • 當個別對象支配樹的 Retained Heap 很大存在明顯傾斜時,可以重點分析占比高的對象支配關系,展開子樹進一步定位到問題根因,如下圖中可看出最終是 SameContentWrapperContainer 對象持有的 ArrayList 過大。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

  • 在 Dominator tree 中展開樹狀圖,可以查看支配關系路徑(與 outgoing reference 的區(qū)別是:如果 X 支配 Y,則 X 釋放后 Y必然可釋放;如果僅僅是 X 引用 Y,可能仍有其他對象引用 Y,X 釋放后 Y 仍不能釋放,所以 Dominator tree 去除了 incoming reference 中大量的冗余信息)。

  • 有些情況下可能并沒有支配起點對象的 Retained Heap 占用很大內(nèi)存(比如 class X 有100個對象,每個對象的 Retained Heap 是10M,則 class X 所有對象實際支配的內(nèi)存是 1G,但可能 Dominator tree 的前20個都是其他class 的對象),這時可以按 class、package、class loader 做聚合,進而定位目標。

  • 下圖中各 GC Roots 所支配的內(nèi)存均不大,這時需要聚合定位爆發(fā)點。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

在 Dominator tree 展現(xiàn)后按 class 聚合,如下圖:

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

可以定位到是 SomeEntry 對象支配內(nèi)存較多,然后結合代碼進一步分析具體原因。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

在一些操作后定位到異常持有 Retained Heap 對象后(如從代碼看對象應該被回收),可以獲取對象的直接支配者,操作方式如下。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

2.3 Histogram 直方圖

注:筆者使用頻率 Top2

功能

  • 羅列每個類實例的數(shù)量、類實例累計內(nèi)存占比,包括自身內(nèi)存占用量(Shallow Heap)及支配對象的內(nèi)存占用量(Retain Heap)。

  • 支持按對象數(shù)量、Retained Heap、Shallow Heap(默認排序)等指標排序;支持按正則過濾;支持按 package、class loader、super class、class 聚類統(tǒng)計,

使用入口:MAT 主界面 → Histogram;注意 Histogram 默認不展現(xiàn) Retained Heap,可以使用計算器圖標計算,如下圖所示。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用場景

  • 有些情況 Dominator tree 無法展現(xiàn)出熱點對象(上文提到 Dominator tree 支配內(nèi)存排名前20的占比均不高,或者按 class 聚合也無明顯熱點對象,此時 Dominator tree 很難做關聯(lián)分析判斷哪類對象占比高),這時可以使用 Histogram 查看所有對象所屬類的分布,快速定位占據(jù) Retained Heap 大頭的類。

使用技巧

  • Integer,String 和 Object[] 一般不直接導致內(nèi)存問題。為更好的組織視圖,可以通過 class loader 或 package 分組進一步聚焦,如下圖。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

Histogram 支持使用正則表達式來過濾。例如,我們可以只展示那些匹配com.q.*的類。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

可以在 Histogram 的某個類繼續(xù)使用 outgoing reference 查看對象分布,進而定位哪些對象是大頭

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

2.4 Leak Suspects

功能:具備自動檢測內(nèi)存泄漏功能,羅列可能存在內(nèi)存泄漏的問題點。

使用入口:一般當存在明顯的內(nèi)存泄漏時,分析完Dump文件后就會展現(xiàn),也可以如下圖在 MAT 主頁 → Leak Suspects。

使用場景:需要查看引用鏈條上占用內(nèi)存較多的可疑對象。這個功能可解決一些基礎問題,但復雜的問題往往幫助有限。

舉例

  • 下圖中 Leak Suspects 視圖展現(xiàn)了兩個線程支配了絕大部分內(nèi)存。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

下圖是點擊上圖中 Keywords 中 "Details" ,獲取實例到 GC Root 的最短路徑、dominator 路徑的細信息。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

2.5 Top Consumers

功能:最大對象報告,可以展現(xiàn)哪些類、哪些 class loader、哪些 package 占用最高比例的內(nèi)存,其功能 Histogram 及 Dominator tree 也都支持。

使用場景:應用程序發(fā)生內(nèi)存泄漏時,查看哪些泄漏的對象通常在 Dump 快照中會占很大的比重。因此,對簡單的問題具有較高的價值。

2.6 綜合案例一

使用工具項:Heap dump overview、Dominator tree、Histogram、Class Loader Explorer(見3.4節(jié))、incoming references(見3.1節(jié))

程序代碼

 
 

package com.q.mat; import java.util.*; import org.objectweb.asm.*; public class ClassLoaderOOMOps extends ClassLoader implements Opcodes { public static void main(final String args[]) throws Exception { new ThreadAndListHolder(); // ThreadAndListHolder 類中會加載大對象 List<ClassLoader> classLoaders = new ArrayList<ClassLoader>(); final String className = "ClassLoaderOOMExample"; final byte[] code = geneDynamicClassBytes(className); // 循環(huán)創(chuàng)建自定義 class loader,并加載 ClassLoaderOOMExample while (true) { ClassLoaderOOMOps loader = new ClassLoaderOOMOps(); Class<?> exampleClass = loader.defineClass(className, code, 0, code.length); //將二進制流加載到內(nèi)存中 classLoaders.add(loader); // exampleClass.getMethods()[0].invoke(null, new Object[]{null}); // 執(zhí)行自動加載類的方法,通過反射調(diào)用main } } private static byte[] geneDynamicClassBytes(String className) throws Exception { ClassWriter cw = new ClassWriter(0); cw.visit(V1_1, ACC_PUBLIC, className, null, "java/lang/Object", null); //生成默認構造方法 MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); //生成構造方法的字節(jié)碼指令 mw.visitVarInsn(ALOAD, 0); mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mw.visitInsn(RETURN); mw.visitMaxs(1, 1); mw.visitEnd(); //生成main方法 mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); //生成main方法中的字節(jié)碼指令 mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mw.visitLdcInsn("Hello world!"); mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mw.visitInsn(RETURN); mw.visitMaxs(2, 2); mw.visitEnd(); //字節(jié)碼生成完成 return cw.toByteArray(); // 獲取生成的class文件對應的二進制流 } }

 

package com.q.mat; import java.util.*; import org.objectweb.asm.*; public class ThreadAndListHolder extends ClassLoader implements Opcodes { private static Thread innerThread1; private static Thread innerThread2; private static final SameContentWrapperContainerProxy sameContentWrapperContainerProxy = new SameContentWrapperContainerProxy(); static { // 啟用兩個線程作為 GC Roots innerThread1 = new Thread(new Runnable() { public void run() { SameContentWrapperContainerProxy proxy = sameContentWrapperContainerProxy; try { Thread.sleep(60 * 60 * 1000); } catch (Exception e) { System.exit(1); } } }); innerThread1.setName("ThreadAndListHolder-thread-1"); innerThread1.start(); innerThread2 = new Thread(new Runnable() { public void run() { SameContentWrapperContainerProxy proxy = proxy = sameContentWrapperContainerProxy; try { Thread.sleep(60 * 60 * 1000); } catch (Exception e) { System.exit(1); } } }); innerThread2.setName("ThreadAndListHolder-thread-2"); innerThread2.start(); } } class IntArrayListWrapper { private ArrayList<Integer> list; private String name; public IntArrayListWrapper(ArrayList<Integer> list, String name) { this.list = list; this.name = name; } } class SameContentWrapperContainer { // 2個Wrapper內(nèi)部指向同一個 ArrayList,方便學習 Dominator tree IntArrayListWrapper intArrayListWrapper1; IntArrayListWrapper intArrayListWrapper2; public void init() { // 線程直接支配 arrayList,兩個 IntArrayListWrapper 均不支配 arrayList,只能線程運行完回收 ArrayList<Integer> arrayList = generateSeqIntList(10 * 1000 * 1000, 0); intArrayListWrapper1 = new IntArrayListWrapper(arrayList, "IntArrayListWrapper-1"); intArrayListWrapper2 = new IntArrayListWrapper(arrayList, "IntArrayListWrapper-2"); } private static ArrayList<Integer> generateSeqIntList(int size, int startValue) { ArrayList<Integer> list = new ArrayList<Integer>(size); for (int i = startValue; i < startValue + size; i++) { list.add(i); } return list; } } class SameContentWrapperContainerProxy { SameContentWrapperContainer sameContentWrapperContainer; public SameContentWrapperContainerProxy() { SameContentWrapperContainer container = new SameContentWrapperContainer(); container.init(); sameContentWrapperContainer = container; } }

 

啟動參數(shù):-Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/gjd/Desktop/dump/heapdump.hprof -XX:-UseCompressedClassPointers -XX:-UseCompressedOops

引用關系圖

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

分析過程

  1. 首先進入 Dominator tree,可以看出是 SameContentWrapperContainerProxy 對象與 main 線程兩者持有99%內(nèi)存不能釋放導致 OOM。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

先來看方向一,在 Heap Dump Overview中可以快速定位到 Number of class loaders 數(shù)達50萬以上,這種基本屬于異常情況,如下圖所示。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用 Class Loader Explorer 分析工具,此時會展現(xiàn)類加載詳情,可以看到有524061個 class loader。我們的案例中僅有ClassLoaderOOMOps 這樣的自定義類加載器,所以很快可以定位到問題。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

如果類加載器較多,不能確定是哪個引發(fā)問題,則可以將所有的 class loader對象按類做聚類,如下圖所示。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

Histogram 會根據(jù) class 聚合,并展現(xiàn)對象數(shù)量及其 Shallow Heap 及 Retained Heap(如Retained Heap項目為空,可以點擊下圖中計算機的圖標并計算 Retained Heap),可以看到 ClassLoaderOOMOps 有524044個對象,其 Retain Heap 占據(jù)了370M以上(上述代碼是100M左右)。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用 incoming references,可以找到創(chuàng)建的代碼位置。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

再來看方向二,同樣在占據(jù)319M內(nèi)存的 Obejct 數(shù)組采用 incoming references 查看引用路徑,也很容易定位到具體代碼位置。并且從下圖中我們看出,Dominator tree 的起點并不一定是 GC根,且通過 Dominator tree 可能無法獲取到最開始的創(chuàng)建路徑,但 incoming references 是可以的。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

3. 對象間依賴詳解及實戰(zhàn)

3.1 References

注:筆者使用頻率 Top2

功能:在對象引用圖中查看某個特定對象的所有引用關系(提供對象對其他對象或基本類型的引用關系,以及被外部其他對象的引用關系)。通過任一對象的直接引用及間接引用詳情(主要是屬性值及內(nèi)存占用),提供完善的依賴鏈路詳情。

使用入口:目標域右鍵 → List objects → with outgoing references/with incoming references.

使用場景

  • outgoing reference:查看對象所引用的對象,并支持鏈式傳遞操作。如查看一個大對象持有哪些內(nèi)容,當一個復雜對象的 Retained Heap 較大時,通過 outgoing reference 可以查看由哪個屬性引發(fā)的。下圖中 A 支配 F,且 F 占據(jù)大量內(nèi)存,但優(yōu)化時 F 的直接支配對象 A 無法修改。可通過 outgoing reference 看關系鏈上 D、B、E、C,并結合業(yè)務邏輯優(yōu)化中間環(huán)節(jié),這依托 dominator tree 是做不到的。

  • incoming reference:查看對象被哪些對象引用,并支持鏈式傳遞操作。如查看一個大對象都被哪些對象引用,下圖中 K 占內(nèi)存大,所以 J 的 Retained Heap 較大,目標是從 GC Roots 摘除 J 引用,但在 Dominator tree 上 J 是樹根,無法獲取其被引用路徑,可通過 incoming reference 查看關系鏈上的 H、X、Y ,并結合業(yè)務邏輯將 J 從 GC Root 鏈摘除。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

3.2 Thread overview

功能:展現(xiàn)轉(zhuǎn)儲 dump 文件是線程執(zhí)行棧、線程棧引用的對象等詳細狀態(tài),也提供各線程的 Retained Heap 等關聯(lián)內(nèi)存信息。

使用入口:MAT 主頁 → Thread overview

使用場景

  • 查看不同線程持有的內(nèi)存占比,定位高內(nèi)存消耗線程(開發(fā)技巧:不要直接使用 Thread 或 Executor 默認線程名避免全部混合在一起,使用線程盡量自命名方便識別,如下圖中 ThreadAndListHolder-thread 是自定義線程名,可以很容易定位到具體代碼)

  • 查看線程的執(zhí)行棧及變量,結合業(yè)務代碼了解線程阻塞在什么地方,以及無法繼續(xù)運行釋放內(nèi)存,如下圖中 ThreadAndListHolder-thread 阻塞在 sleep 方法。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

3.3 Path To GC Roots

功能:提供任一對象到 GC Root 的路徑詳情。

使用入口:目標域右鍵 → Path To GC Roots

使用場景:有時你確信已經(jīng)處理了大的對象集合但依然無法回收,該功能能快速定位異常對象不能被 GC 回收的原因,直擊異常對象到 GC Root 的引用路徑。比 incoming reference 的優(yōu)勢是屏蔽掉很多不需關注的引用關系,比 Dominator tree 的優(yōu)勢是可以得到更全面的信息。

小技巧:在排查內(nèi)存泄漏時,建議選擇 exclude all phantom/weak/soft etc.references 排除虛引用/弱引用/軟引用等的引用鏈,因為被虛引用/弱引用/軟引用的對象可以直接被 GC 給回收,聚焦在對象是否還存在 Strong 引用鏈即可。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

3.4 class loader 分析

功能

  • 查看堆中所有 class loader 的使用情況(入口:MAT 主頁菜單藍色桶圖標 → Java Basics → Class Loader Explorer)。

  • 查看堆中被不同class loader 重復加載的類(入口:MAT 主頁菜單藍色桶圖標 → Java Basics → Duplicated Classes)。

使用場景

  • 當從 Heap dump overview 了解到系統(tǒng)中 class loader 過多,導致占用內(nèi)存異常時進入更細致的分析定位根因時使用。

  • 解決 NoClassDefFoundError 問題或檢測 jar 包是否被重復加載

具體使用方法在 2.6 及 3.5 兩節(jié)的案例中有介紹。

3.5 綜合案例二

使用工具項:class loader(重復類檢測)、inspector、正則檢索。

異?,F(xiàn)象 :運行時報 NoClassDefFoundError,在 classpath 中有兩個不同版本的同名類。

分析過程

  1. 進入 MAT 已加載的重復類檢測功能,方式如下圖。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

可以看到所有重復的類,以及相關的類加載器,如下圖。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

  • 根據(jù)類名,在<Regex>框中輸入類名可以過濾無效信息。

  • 選中目標類,通過Inspector視圖,可以看到被加載的類具體是在哪個jar包里。(本例中重復的類是被 URLClassloader 加載的,右鍵點擊 “_context” 屬性,最后點擊 “Go Into”,在彈出的窗口中的屬性 “_war” 值是被加載類的具體包位置)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

4. 對象狀態(tài)詳解及實戰(zhàn)

4.1 inspector

功能:MAT 通過 inspector 面板展現(xiàn)對象的詳情信息,如靜態(tài)屬性值及實例屬性值、內(nèi)存地址、類繼承關系、package、class loader、GC Roots 等詳情數(shù)據(jù)。

使用場景

  • 當內(nèi)存使用量與業(yè)務邏輯有較強關聯(lián)的場景,通過 inspector 可以通過查看對象具體屬性值。比如:社交場景中某個用戶對象的好友列表異常,其 List 長度達到幾億,通過 inspector 面板獲取到異常用戶 ID,進而從業(yè)務視角繼續(xù)排查屬于哪個用戶,本里可能有系統(tǒng)賬號,與所有用戶是好友。

  • 集合等類型的使用會較多,如查看 ArrayList 的 size 屬性也就了解其大小。

舉例:下圖中左邊的 Inspector 窗口展現(xiàn)了地址 0x125754cf8 的 ArrayList 實例詳情,包括 modCount 等并不會在 outgoing references 展現(xiàn)的基本屬性。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

4.2 集合狀態(tài)

功能:幫助更直觀的了解系統(tǒng)的內(nèi)存使用情況,查找浪費的內(nèi)存空間。

使用入口:MAT 主頁 → Java Collections → 填充率/Hash沖突等功能。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用場景

  • 通過對 ArrayList 或數(shù)組等集合類對象按填充率聚類,定位稀疏或空集合類對象造成的內(nèi)存浪費。

  • 通過 HashMap 沖突率判定 hash 策略是否合理。

具體使用方法在 4.3 節(jié)案例詳細介紹。

4.3 綜合案例三

使用工具項:Dominator tree、Histogram、集合 ratio。

異?,F(xiàn)象 :程序 OOM,且 Dominator tree 無大對象,通過 Histogram 了解到多個 ArrayList 占據(jù)大量內(nèi)存,期望通過減少 ArrayList 優(yōu)化程序。

程序代碼

 
 

package com.q.mat; import java.util.ArrayList; import java.util.List; public class ListRatioDemo { public static void main(String[] args) { for(int i=0;i<10000;i++){ Thread thread = new Thread(new Runnable() { public void run() { HolderContainer holderContainer1 = new HolderContainer(); try { Thread.sleep(1000 * 1000 * 60); } catch (Exception e) { System.exit(1); } } }); thread.setName("inner-thread-" + i); thread.start(); } } } class HolderContainer { ListHolder listHolder1 = new ListHolder().init(); ListHolder listHolder2 = new ListHolder().init(); } class ListHolder { static final int LIST_SIZE = 100 * 1000; List<String> list1 = new ArrayList(LIST_SIZE); // 5%填充 List<String> list2 = new ArrayList(LIST_SIZE); // 5%填充 List<String> list3 = new ArrayList(LIST_SIZE); // 15%填充 List<String> list4 = new ArrayList(LIST_SIZE); // 30%填充 public ListHolder init() { for (int i = 0; i < LIST_SIZE; i++) { if (i < 0.05 * LIST_SIZE) { list1.add("" + i); list2.add("" + i); } if (i < 0.15 * LIST_SIZE) { list3.add("" + i); } if (i < 0.3 * LIST_SIZE) { list4.add("" + i); } } return this; } }

分析過程

  1. 使用 Dominator tree 查看并無高占比起點。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

使用 Histogram 定位到 ListHolder 及 ArrayList 占比過高,經(jīng)過業(yè)務分析很多 List 填充率很低,不會浪費內(nèi)存。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

查看 ArrayList 的填充率,MAT 首頁 → Java Collections → Collection Fill Ratio。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

查看類型填寫 java.util.ArrayList。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

從結果可以看出絕大部分 ArrayList 初始申請長度過大。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

5. 按條件檢索詳解及實戰(zhàn)

5.1 OQL

功能:提供一種類似于SQL的對象(類)級別統(tǒng)一結構化查詢語言,根據(jù)條件對堆中對象進行篩選。

語法

 
 

SELECT * FROM [ INSTANCEOF ] <class_name> [ WHERE <filter-expression> ]

  • Select 子句可以使用“*”,查看結果對象的引用實例(相當于 outgoing references);可以指定具體的內(nèi)容,如 Select OBJECTS v.elementData from xx 是返回的結果是完整的對象,而不是簡單的對象描述信息);可以使用 Distinct 關鍵詞去重。

  • From 指定查詢范圍,一般指定類名、正則表達式、對象地址。

  • Where 用來指定篩選條件。

  • 全部語法詳見:OQL 語法

  • 未支持的核心功能:group by value,如果有需求可以先導出結果到 csv 中,再使用 awk 等腳本工具分析即可。

例子:查找 size=0 且未使用過的 ArrayList:select * from java.util.ArrayList where size=0 and modCount=0。

使用場景

  • 一般比較復雜的問題會使用 OQL,而且這類問題往往與業(yè)務邏輯有較大關系。比如大量的小對象整體占用內(nèi)存高,但預期小對象應該不會過多(比如達到百萬個),一個一個看又不現(xiàn)實,可以采用 OQL 查詢導出數(shù)據(jù)排查。

例如:微服務的分布式鏈路追蹤系統(tǒng),采集各服務所有接口名,共計200個服務卻采集到了200萬個接口名(一個服務不會有1萬個接口),這時直接在 List 中一個個查看很難定位,可以直接用 OQL 導出,定位哪個服務接口名收集異常(如把 URL 中 ID 也統(tǒng)計到接口中了)

5.2 檢索及篩選

功能:本文第二章內(nèi)存分布,第三章對象間依賴的眾多功能,均支持按字符串檢索、按正則檢索等操作。

使用場景:在使用 Histogram、Thread overview 等功能時,可以進一步添加字符串匹配、正則匹配條件過濾縮小排查范圍。

5.3 按地址尋址

功能:根據(jù)對象的虛擬內(nèi)存十六進制地址查找對象。

使用場景:僅知道地址并希望快速查看對象做后續(xù)分析時使用,其余可以直接使用 outgoing reference 了解對象信息。

5.4 綜合案例四

使用工具項:OQL、Histogram、incoming references

異常現(xiàn)象及目的 :程序占用內(nèi)存高,存在默認初始化較長的 ArrayList,需分析 ArrayList 被使用的占比,通過數(shù)據(jù)支撐是否采用懶加載模式,并分析具體哪塊代碼創(chuàng)建了空 ArrayList。

程序代碼

 
 

public class EmptyListDemo { public static void main(String[] args) { EmptyValueContainerList emptyValueContainerList = new EmptyValueContainerList(); FilledValueContainerList filledValueContainerList = new FilledValueContainerList(); System.out.println("start sleep..."); try { Thread.sleep(50 * 1000 * 1000); } catch (Exception e) { System.exit(1); } } } class EmptyValueContainer { List<Integer> value1 = new ArrayList(10); List<Integer> value2 = new ArrayList(10); List<Integer> value3 = new ArrayList(10); } class EmptyValueContainerList { List<EmptyValueContainer> list = new ArrayList(500 * 1000); public EmptyValueContainerList() { for (int i = 0; i < 500 * 1000; i++) { list.add(new EmptyValueContainer()); } } } class FilledValueContainer { List<Integer> value1 = new ArrayList(10); List<Integer> value2 = new ArrayList(10); List<Integer> value3 = new ArrayList(10); public FilledValueContainer init() { value1.addAll(Arrays.asList(1, 3, 5, 7, 9)); value2.addAll(Arrays.asList(2, 4, 6, 8, 10)); value1.addAll(Arrays.asList(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); return this; } } class FilledValueContainerList { List<FilledValueContainer> list = new ArrayList(500); public FilledValueContainerList() { for (int i = 0; i < 500; i++) { list.add(new FilledValueContainer().init()); } } }

分析過程

  1. 內(nèi)存中有50萬個 capacity = 10 的空 ArrayList 實例。我們分析下這些對象的占用內(nèi)存總大小及對象創(chuàng)建位置,以便分析延遲初始化(即直到使用這些對象的時候才將之實例化,否則一直為null)是否有必要。

  2. 使用 OQL 查詢出初始化后未被使用的 ArrayList(size=0 且 modCount=0),語句如下圖??梢钥闯龉?150 萬個空 ArrayList,這些對象屬于浪費內(nèi)存。我們接下來計算下總計占用多少內(nèi)存,并根據(jù)結果看是否需要優(yōu)化。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

計算 150萬 ArrayList占內(nèi)存總量,直接點擊右上方帶黃色箭頭的 Histogram 圖標,這個圖標是在選定的結果再用直方圖展示,總計支配了120M 左右內(nèi)存(所以這里點擊結果,不包含 modCount 或 size 大于0的 ArrayList 對象)。這類在選定結果繼續(xù)分析很多功能都支持,如正則檢索、Histogram、Dominator tree等等。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

查看下圖 ArrayList 的具體來源,可用 incoming references,下圖中顯示了清晰的對象創(chuàng)建路徑。

java mat,程序員,編程,Java,jvm,java,算法

?

編輯切換為居中

添加圖片注釋,不超過 140 字(可選)

總結

至此本文講解了 MAT 各項工具的功能、使用方法、適用場景,也穿插了4個實戰(zhàn)案例,熟練掌握對分析 JVM 內(nèi)存問題大有裨益,尤其是各種功能的組合使用。

??????資源獲取:
大家 點贊、收藏、關注、評論啦 、 查看???????????? 微信公眾號獲取聯(lián)系方式????????????
精彩專欄推薦訂閱:下方專欄????????????????
每天學四小時:Java+Spring+JVM+分布式高并發(fā),架構師指日可待

java mat,程序員,編程,Java,jvm,java,算法文章來源地址http://www.zghlxwxcb.cn/news/detail-699961.html

到了這里,關于一文深度講解JVM 內(nèi)存分析工具 MAT及實踐(建議收藏)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • JVM:性能監(jiān)控工具分析和線上問題排查實踐

    JVM:性能監(jiān)控工具分析和線上問題排查實踐

    在日常開發(fā)過程中,多少都會碰到一些jvm相關的問題,比如: 內(nèi)存溢出、內(nèi)存泄漏、cpu利用率飆升到100%、線程死鎖、應用異常宕機 等。 在這個日益內(nèi)卷的環(huán)境,如何運用好工具分析jvm問題,成為每個java攻城獅必備的技能。所以白夢特意整理了 jdk自帶分析工具的使用 ,以及

    2024年01月19日
    瀏覽(27)
  • Java 內(nèi)存溢出(二)使用 MAT 分析 .hprof 內(nèi)存映像文件

    Java 內(nèi)存溢出(二)使用 MAT 分析 .hprof 內(nèi)存映像文件

    .hprof 文件: 是 java 項目的 Heap Dump 文件,也叫內(nèi)存映像文件、內(nèi)存快照文件,可以存放一個 java 進程在某個時間點的內(nèi)存快照。生成 Heap Dump 文件的方式有兩種:一是使用 jmap 命令手動導出,二是啟動腳本中添加 -XX:+HeapDumpOnOutOfMemoryError 參數(shù)自動導出。本文中只涉及第二種。

    2023年04月08日
    瀏覽(36)
  • eclipse memory Analyzer(MAT) 內(nèi)存泄漏分析

    eclipse memory Analyzer(MAT) 內(nèi)存泄漏分析

    ? 1.1軟件下載 ????????Eclipse IDE,它非常有用。因為Memory Analyzer在分析堆內(nèi)存的時候比較耗費內(nèi)存,而Eclipse IDE本身又是比較耗費內(nèi)存的,所以推薦使用獨立安裝的Memory Analyzer。 安裝包地址:https://www.eclipse.org/mat/downloads.php 獨立安裝的Memory Analyzer的獨立版本所需的最低Ja

    2024年02月14日
    瀏覽(42)
  • 【開發(fā)篇】四、MAT堆內(nèi)存分析(Memory Analyzer Tool)

    【開發(fā)篇】四、MAT堆內(nèi)存分析(Memory Analyzer Tool)

    內(nèi)存溢出后,分析泄露的思路是: 在OOM前,將整個堆內(nèi)存保存成一個hprof文件 MAT打開hprof文件,MAT自行分析可疑對象 添加JVM參數(shù): 模擬個OOM: 在MAT里打開hprof文件: 報錯: 很清晰了, 下載JDK17或者換一個低版本的MAT(適配你配置的JDK版本的MAT) ,我當前是JDK11,下載地址【

    2024年01月19日
    瀏覽(23)
  • android 如何分析應用的內(nèi)存(十七)——使用MAT查看Android堆

    android 如何分析應用的內(nèi)存(十七)——使用MAT查看Android堆

    前一篇文章,介紹了使用Android profiler中的memory profiler來查看Android的堆情況。 如Android 堆中有哪些對象,這些對象的引用情況是什么樣子的。 可是我們依然面臨一個比較嚴峻的挑戰(zhàn):不管是app開發(fā)者,還是內(nèi)存分析者而言,堆中的對象,非常之多,不僅有Android 原生的類,還

    2024年02月13日
    瀏覽(26)
  • 【linux命令講解大全】045.網(wǎng)絡數(shù)據(jù)分析利器:深度解讀 tcpdump 抓包工具的使用方法

    tcpdump是一款在Linux上的抓包工具,用于嗅探網(wǎng)絡數(shù)據(jù)。 補充說明 tcpdump命令是一款抓包、嗅探器工具。它可以打印所有經(jīng)過網(wǎng)絡接口的數(shù)據(jù)包的頭信息,并可使用-w選項將數(shù)據(jù)包保存到文件中,以便以后進行分析。 語法 選項 -a:嘗試將網(wǎng)絡和廣播地址轉(zhuǎn)換成名稱 -c 數(shù)據(jù)包數(shù)

    2024年02月10日
    瀏覽(47)
  • JVM內(nèi)存模型深度解讀

    JVM內(nèi)存模型深度解讀

    ????????JVM(Java Virtual Machine,Java虛擬機)對于Java開發(fā)者和運行 Java 應用程序而言至關重要。其重要性主要體現(xiàn)在跨平臺性、內(nèi)存管理和垃圾回收、性能優(yōu)化、安全性和穩(wěn)定性、故障排查與性能調(diào)優(yōu)等方面。今天就下學習一下 JVM 的內(nèi)存模型。 ????????JVM 內(nèi)存模型(

    2024年03月19日
    瀏覽(30)
  • 深入理解JVM——垃圾回收與內(nèi)存分配機制詳細講解

    深入理解JVM——垃圾回收與內(nèi)存分配機制詳細講解

    所謂垃圾回收,也就是要回收已經(jīng)“死了”的對象。 那我們?nèi)绾闻袛嗄男ο蟆按婊睢保男┮呀?jīng)“死去”呢? 給對象中添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器就加一;當引用失效時,計數(shù)器就減1;任何時刻計數(shù)器為0的對象就是不可能再被使用的。 但是

    2024年02月12日
    瀏覽(26)
  • JVM 內(nèi)存大對象監(jiān)控和優(yōu)化實踐

    JVM 內(nèi)存大對象監(jiān)控和優(yōu)化實踐

    作者:vivo 互聯(lián)網(wǎng)服務器團隊 - Liu Zhen、Ye Wenhao 服務器內(nèi)存問題是影響應用程序性能和穩(wěn)定性的重要因素之一,需要及時排查和優(yōu)化。本文介紹了某核心服務內(nèi)存問題排查與解決過程。首先在JVM與大對象優(yōu)化上進行了有效的實踐,其次在故障轉(zhuǎn)移與大對象監(jiān)控上提出了可靠的

    2024年02月10日
    瀏覽(23)
  • 06-JVM對象內(nèi)存回收機制深度剖析

    06-JVM對象內(nèi)存回收機制深度剖析

    上一篇:05-JVM內(nèi)存分配機制深度剖析 堆中幾乎放著所有的對象實例,對堆垃圾回收前的第一步就是要判斷哪些對象已經(jīng)死亡( 即不能再被任何途徑使用的對象 )。 給對象中添加一個引用計數(shù)器,每當有一個地方引用它,計數(shù)器就加1;當引用失效,計數(shù)器就減1;任何時候計

    2024年02月09日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包