<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                我們知道,在存儲用戶輸入的密碼時,會使用一些 hash 算法對密碼進行加工,比如 SHA-1。這些信息同樣不允許在日志輸出里出現,必須做脫敏處理,但是對于一個擁有系統權限的攻擊者來說,這些防護依然是不夠的。攻擊者可能會直接從內存中獲取明文數據,尤其是對于 Java 來說,由于提供了 jmap 這一類非常方便的工具,可以把整個堆內存的數據 dump 下來。 比如,“我的世界”這一類使用 Java 開發的游戲,會比其他語言的游戲更加容易破解一些,所以我們在 JVM 中,如果把密碼存儲為 char 數組,其安全性會稍微高一些。 這是一把雙刃劍,在保證安全的前提下,我們也可以借助一些外部的分析工具,幫助我們方便的找到問題根本。 有兩種方式來獲取內存的快照。我們前面提到過,通過配置一些參數,可以在發生 OOM 的時候,被動 dump 一份堆棧信息,這是一種;另一種,就是通過 jmap 主動去獲取內存的快照。 jmap 命令在 Java 9 之后,使用 jhsdb 命令替代,它們在用法上,區別不大。注意,這些命令本身會占用操作系統的資源,在某些情況下會造成服務響應緩慢,所以不要頻繁執行。 ``` jmap?-dump:format=b,file=heap.bin?37340 jhsdb?jmap??--binaryheap?--pid??37340 ``` #### 1. 工具介紹 有很多工具能夠幫助我們來分析這份內存快照。在前面已多次提到 VisualVm 這個工具,它同樣可以加載和分析這份 dump 數據,雖然比較“寒磣”。 專業的事情要有專業的工具來做,今天要介紹的是一款專業的開源分析工具,即 MAT。 MAT 工具是基于 Eclipse 平臺開發的,本身是一個 Java 程序,所以如果你的堆快照比較大的話,則需要一臺內存比較大的分析機器,并給 MAT 本身加大初始內存,這個可以修改安裝目錄中的 MemoryAnalyzer.ini 文件。 來看一下 MAT 工具的截圖,主要的功能都體現在工具欄上了。其中,默認的啟動界面,展示了占用內存最高的一些對象,并有一些常用的快捷方式。通常,發生內存泄漏的對象,會在快照中占用比較大的比重,分析這些比較大的對象,是我們切入問題的第一步。 ![](https://img.kancloud.cn/7c/d1/7cd101bbea66456619d717d9186c9d3b_838x856.jpg) 點擊對象,可以瀏覽對象的引用關系,這是一個非常有用的功能: * outgoing references 對象的引出 * incoming references ?對象的引入 **path to GC Roots** 這是快速分析的一個常用功能,顯示和 GC Roots 之間的路徑。 ![](https://img.kancloud.cn/e0/05/e0057a2f1136c16945be2c9bd6cfe7ff_902x421.jpg) 另外一個比較重要的概念,就是淺堆(Shallow Heap)和深堆(Retained Heap),在 MAT 上經常看到這兩個數值。 ![](https://img.kancloud.cn/41/f2/41f2f4df435a94a6daca903123d2575f_661x400.jpg) 淺堆代表了對象本身的內存占用,包括對象自身的內存占用,以及“為了引用”其他對象所占用的內存。 深堆是一個統計結果,會循環計算引用的具體對象所占用的內存。但是深堆和“對象大小”有一點不同,深堆指的是一個對象被垃圾回收后,能夠釋放的內存大小,這些被釋放的對象集合,叫做保留集(Retained Set)。 ![](https://img.kancloud.cn/3e/c3/3ec3773ac532f9c08ecdaf93d3f18d16_813x302.png) 如上圖所示,A 對象淺堆大小 1 KB,B 對象 2 KB,C 對象 100 KB。A 對象同時引用了 B 對象和 C 對象,但由于 C 對象也被 D 引用,所以 A 對象的深堆大小為 3 KB(1 KB + 2 KB)。 A 對象大小(1 KB + 2 KB + 100 KB)> A 對象深堆 > A 對象淺堆。 #### 2. 代碼示例 ``` import?java.util.ArrayList; import?java.util.HashMap; import?java.util.List; import?java.util.Map; import?java.util.stream.IntStream; public?class?Objects4MAT?{ ????static?class?A4MAT?{ ????????B4MAT?b4MAT?=?new?B4MAT(); ????} ????static?class?B4MAT?{ ????????C4MAT?c4MAT?=?new?C4MAT(); ????} ????static?class?C4MAT?{ ????????List<String>?list?=?new?ArrayList<>(); ????} ????static?class?DominatorTreeDemo1?{ ????????DominatorTreeDemo2?dominatorTreeDemo2; ????????public?void?setValue(DominatorTreeDemo2?value)?{ ????????????this.dominatorTreeDemo2?=?value; ????????} ????} ????static?class?DominatorTreeDemo2?{ ????????DominatorTreeDemo1?dominatorTreeDemo1; ????????public?void?setValue(DominatorTreeDemo1?value)?{ ????????????this.dominatorTreeDemo1?=?value; ????????} ????} ????static?class?Holder?{ ????????DominatorTreeDemo1?demo1?=?new?DominatorTreeDemo1(); ????????DominatorTreeDemo2?demo2?=?new?DominatorTreeDemo2(); ????????Holder()?{ ????????????demo1.setValue(demo2); ????????????demo2.setValue(demo1); ????????} ????????private?boolean?aBoolean?=?false; ????????private?char?aChar?=?'\0'; ????????private?short?aShort?=?1; ????????private?int?anInt?=?1; ????????private?long?aLong?=?1L; ????????private?float?aFloat?=?1.0F; ????????private?double?aDouble?=?1.0D; ????????private?Double?aDouble_2?=?1.0D; ????????private?int[]?ints?=?new?int[2]; ????????private?String?string?=?"1234"; ????} ????Runnable?runnable?=?()?->?{ ????????Map<String,?A4MAT>?map?=?new?HashMap<>(); ????????IntStream.range(0,?100).forEach(i?->?{ ????????????byte[]?bytes?=?new?byte[1024?*?1024]; ????????????String?str?=?new?String(bytes).replace('\0',?(char)?i); ????????????A4MAT?a4MAT?=?new?A4MAT(); ????????????a4MAT.b4MAT.c4MAT.list.add(str); ????????????map.put(i?+?"",?a4MAT); ????????}); ????????Holder?holder?=?new?Holder(); ????????try?{ ????????????//sleep?forever?,?retain?the?memory ????????????Thread.sleep(Integer.MAX_VALUE); ????????}?catch?(InterruptedException?e)?{ ????????????e.printStackTrace(); ????????} ????}; ????void?startHugeThread()?throws?Exception?{ ????????new?Thread(runnable,?"huge-thread").start(); ????} ????public?static?void?main(String[]?args)?throws?Exception?{ ????????Objects4MAT?objects4MAT?=?new?Objects4MAT(); ????????objects4MAT.startHugeThread(); ????} } ``` * 2.1. 代碼介紹 我們以一段代碼示例 Objects4MAT,來具體看一下 MAT 工具的使用。代碼創建了一個新的線程 "huge-thread",并建立了一個引用的層級關系,總的內存大約占用 100 MB。同時,demo1 和 demo2 展示了一個循環引用的關系。最后,使用 sleep 函數,讓線程永久阻塞住,此時整個堆處于一個相對“靜止”的狀態。 ![](https://img.kancloud.cn/34/4d/344db6321f37ae799cd3347fcd7342d8_698x448.png) 如果你是在本地啟動的示例代碼,則可以使用 Accquire 的方式來獲取堆快照。 ![](https://img.kancloud.cn/f3/40/f340bba7154770668501ee79fd247e36_695x360.jpg) * 2.2. 內存泄漏檢測 如果問題特別突出,則可以通過 Find Leaks 菜單快速找出問題。 ![](https://img.kancloud.cn/0b/c4/0bc4be5c5b61a82a6ec63620c4a68017_822x443.jpg) 如下圖所示,展示了名稱叫做 huge-thread 的線程,持有了超過 96% 的對象,數據被一個 HashMap 所持有。 ![](https://img.kancloud.cn/51/c9/51c9233fad0c4dc8c0a566faaa7adc72_903x570.jpg) 對于特別明顯的內存泄漏,在這里能夠幫助我們迅速定位,但通常內存泄漏問題會比較隱蔽,我們需要更加復雜的分析。 * 2.3. 支配樹視圖 支配樹視圖對數據進行了歸類,體現了對象之間的依賴關系。如圖,我們通常會根據“深堆”進行倒序排序,可以很容易的看到占用內存比較高的幾個對象,點擊前面的箭頭,即可一層層展開支配關系。 圖中顯示的是其中的 1 MB 數據,從左側的 inspector 視圖,可以看到這 1 MB 的 byte 數組具體內容。 ![](https://img.kancloud.cn/ee/ef/eeef4ebd7fbd26ddf81f1ebdcdc19753_915x617.jpg) 從支配樹視圖同樣能夠找到我們創建的兩個循環依賴,但它們并沒有顯示這個過程。 ![](https://img.kancloud.cn/1c/3d/1c3da921661e26600e7c6ff9b7756333_859x385.jpg) 支配樹視圖的概念有一點點復雜,我們只需要了解這個概念即可。 ![](https://img.kancloud.cn/b4/91/b491afd86bc6a733d0a0c94b911f5485_907x477.png) 如上圖,左邊是引用關系,右邊是支配樹視圖。可以看到 A、B、C 被當作是“虛擬”的根,支配關系是可傳遞的,因為 C 支配 E,E 支配 G,所以 C 也支配 G。 另外,到對象 C 的路徑中,可以經過 A,也可以經過 B,因此對象 C 的直接支配者也是根對象。同理,對象 E 是 H 的支配者。 我們再來看看比較特殊的 D 和 F。對象 F 與對象 D 相互引用,因為到對象 F 的所有路徑必然經過對象 D,因此,對象 D 是對象 F 的直接支配者。 可以看到支配樹視圖并不一定總是能看到對象的真實應用關系,但對我們分析問題的影響并不是很大。 這個視圖是非常好用的,甚至可以根據 package 進行歸類,對目標類的查找也是非常快捷的。 ![](https://img.kancloud.cn/54/eb/54ebab058cf0c517b80902164ab627b0_715x406.jpg) 編譯下面這段代碼,可以展開視圖,實際觀測一下支配樹,這和我們上面介紹的是一致的。 ``` public?class?DorminatorTreeDemo?{ ????static?class?A?{ ????????C?c; ????????byte[]?data?=?new?byte[1024?*?1024?*?2]; ????} ????static?class?B?{ ????????C?c; ????????byte[]?data?=?new?byte[1024?*?1024?*?3]; ????} ????static?class?C?{ ????????D?d; ????????E?e; ????????byte[]?data?=?new?byte[1024?*?1024?*?5]; ????} ????static?class?D?{ ????????F?f; ????????byte[]?data?=?new?byte[1024?*?1024?*?7]; ????} ????static?class?E?{ ????????G?g; ????????byte[]?data?=?new?byte[1024?*?1024?*?11]; ????} ????static?class?F?{ ????????D?d; ????????H?h; ????????byte[]?data?=?new?byte[1024?*?1024?*?13]; ????} ????static?class?G?{ ????????H?h; ????????byte[]?data?=?new?byte[1024?*?1024?*?17]; ????} ????static?class?H?{ ????????byte[]?data?=?new?byte[1024?*?1024?*?19]; ????} ????A?makeRef(A?a,?B?b)?{ ????????C?c?=?new?C(); ????????D?d?=?new?D(); ????????E?e?=?new?E(); ????????F?f?=?new?F(); ????????G?g?=?new?G(); ????????H?h?=?new?H(); ????????a.c?=?c; ????????b.c?=?c; ????????c.e?=?e; ????????c.d?=?d; ????????d.f?=?f; ????????e.g?=?g; ????????f.d?=?d; ????????f.h?=?h; ????????g.h?=?h; ????????return?a; ????} ????static?A?a?=?new?A(); ????static?B?b?=?new?B(); ????public?static?void?main(String[]?args)?throws?Exception?{ ????????new?DorminatorTreeDemo().makeRef(a,?b); ????????Thread.sleep(Integer.MAX_VALUE); ????} } ``` ![](https://img.kancloud.cn/c6/c8/c6c856dc5b1ab7273d7142c753dc7063_889x451.jpg) * 2.4. 線程視圖 想要看具體的引用關系,可以通過線程視圖。我們在第 5 講,就已經了解了線程其實是可以作為 GC Roots 的。如圖展示了線程內對象的引用關系,以及方法調用關系,相對比 jstack 獲取的棧 dump,我們能夠更加清晰地看到內存中具體的數據。 如下圖,我們找到了 huge-thread,依次展開找到 holder 對象,可以看到循環依賴已經陷入了無限循環的狀態。這在查看一些 Java 對象的時候,經常發生,不要感到奇怪。 ![](https://img.kancloud.cn/96/31/9631ef75fc56429e4a20a10552a9a4d2_816x584.jpg) * 2.5. 柱狀圖視圖 我們返回頭來再看一下柱狀圖視圖,可以看到除了對象的大小,還有類的實例個數。結合 MAT 提供的不同顯示方式,往往能夠直接定位問題。也可以通過正則過濾一些信息,我們在這里輸入 MAT,過濾猜測的、可能出現問題的類,可以看到,創建的這些自定義對象,不多不少正好一百個。 ![](https://img.kancloud.cn/9e/79/9e79a63005c5d97995cd964ff57f1b59_786x328.jpg) 右鍵點擊類,然后選擇 incoming,這會列出所有的引用關系。 ![](https://img.kancloud.cn/e2/8b/e28b8a826506dff0bc03dc9e0b53de40_779x420.jpg) 再次選擇某個引用關系,然后選擇菜單“Path To GC Roots”,即可顯示到 GC Roots 的全路徑。通常在排查內存泄漏的時候,會選擇排除虛弱軟等引用。 ![](https://img.kancloud.cn/05/0c/050c9d9ef9a45b143ffd97d89b9790a7_779x371.jpg) 使用這種方式,即可在引用之間進行跳轉,方便的找到所需要的信息。 ![](https://img.kancloud.cn/6b/d3/6bd391b4e635801372c15ac1bff8ab06_779x141.jpg) 再介紹一個比較高級的功能。 我們對于堆的快照,其實是一個“瞬時態”,有時候僅僅分析這個瞬時狀態,并不一定能確定問題,這就需要對兩個或者多個快照進行對比,來確定一個增長趨勢。 ![](https://img.kancloud.cn/d2/61/d261e2fa7209c02b84f9a270c262a8db_786x479.jpg) 可以將代碼中的 100 改成 10 或其他數字,再次 dump 一份快照進行比較。如圖,通過分析某類對象的增長,即可輔助問題定位。 #### 3. 高級功能—OQL MAT 支持一種類似于 SQL ?的查詢語言 OQL(Object Query Language),這個查詢語言 VisualVM 工具也支持。 ![](https://img.kancloud.cn/c1/9d/c19d22dca7721569a6928c7c21e1e3f8_659x375.jpg) 以下是幾個例子,你可以實際實踐一下。 查詢 A4MAT 對象 ``` SELECT?*?FROM??Objects4MAT$A4MAT ``` 正則查詢 MAT 結尾的對象: ``` SELECT?*?FROM?".*MAT" ``` 查詢 String 類的 char 數組: ``` SELECT?OBJECTS?s.value?FROM?java.lang.String?s? SELECT?OBJECTS?mat.b4MAT?FROM??Objects4MAT$A4MAT?mat ``` 根據內存地址查找對象: ``` select?*?from?0x55a034c8 ``` 使用 INSTANCEOF 關鍵字,查找所有子類: ``` SELECT?*?FROM?INSTANCEOF?java.util.AbstractCollection ``` 查詢長度大于 1000 的 byte 數組: ``` SELECT?*?FROM?byte[]?s?WHERE?s.@length>1000 ``` 查詢包含 java 字樣的所有字符串: ``` SELECT?*?FROM?java.lang.String?s?WHERE?toString(s)?LIKE?".*java.*" ``` 查找所有深堆大小大于 1 萬的對象: ``` SELECT?*?FROM?INSTANCEOF?java.lang.Object?o?WHERE?o.@retainedHeapSize>10000 ``` 如果你忘記這些屬性的名稱的話,MAT 是可以自動補全的。 ![](https://img.kancloud.cn/f1/ee/f1eef708466a14012fb7942d83d64d8c_780x307.jpg) OQL 有比較多的語法和用法,若想深入了解,可參考這里。 一般,我們使用上面這些簡單的查詢語句就夠用了。 OQL 還有一個好處,就是可以分享。如果你和同事同時在分析一個大堆,不用告訴他先點哪一步、再點哪一步,共享給他一個 OQL 語句就可以了。 如下圖,MAT 貼心的提供了復制 OQL 的功能,但是用在其他快照上,不會起作用,因為它復制的是如下的內容。 ![](https://img.kancloud.cn/78/78/7878ce824521862879ceb6a7c50f06f8_791x437.jpg) #### 4. 小結 這一講我們介紹了 MAT 工具的使用,其是用來分析內存快照的;在最后,簡要介紹了 OQL 查詢語言。 在 Java 9 以前的版本中,有一個工具 jhat,可以以 html 的方式顯示堆棧信息,但和 VisualVm 一樣,都太過于簡陋,推薦使用 MAT 工具。 我們把問題設定為內存泄漏,但其實 OOM 或者頻繁 GC 不一定就是內存泄漏,它也可能是由于某次或者某批請求頻繁而創建了大量對象,所以一些嚴重的、頻繁的 GC 問題也能在這里找到原因。有些情況下,占用內存最多的對象,并不一定是引起內存泄漏問題的元兇,但我們也有一個比較通用的分析過程。 并不是所有的堆都值得分析的,我們在做這個耗時的分析之前,需要有個依據。比如,經過初步調優之后,GC 的停頓時間還是較長,則需要找到頻繁 GC 的原因;再比如,我們發現了內存泄漏,需要找到是誰在搞鬼。 首先,我們高度關注快照載入后的初始分析,占用內存高的 topN 對象,大概率是問題產生者。 對照自己的代碼,首先要分析的,就是產生這些大對象的邏輯。舉幾個實際發生的例子。有一個 Spring Boot 應用,由于啟用了 Swagger 文檔生成器,但是由于它的 API 關系非常復雜,嵌套層次又非常深(每次要產生幾百 M 的文檔!),結果請求幾次之后產生了內存溢出,這在 MAT 上就能夠一眼定位到問題;而另外一個應用,在讀取數據庫的時候使用了分頁,但是 pageSize 并沒有做一些范圍檢查,結果在請求一個較大分頁的時候,使用 fastjson 對獲取的數據進行加工,直接 OOM。 如果不能通過大對象發現問題,則需要對快照進行深入分析。使用柱狀圖和支配樹視圖,配合引入引出和各種排序,能夠對內存的使用進行整體的摸底。由于我們能夠看到內存中的具體數據,排查一些異常數據就容易得多。 可以在程序運行的不同時間點,獲取多份內存快照,對比之后問題會更加容易發現。我們還是用一個例子來看。有一個應用,使用了 Kafka 消息隊列,開了一般大小的消費緩沖區,Kafka 會復用這個緩沖區,按理說不應該有內存問題,但是應用卻頻繁發生 GC。通過對比請求高峰和低峰期間的內存快照,我們發現有工程師把消費數據放入了另外一個 “內存隊列”,寫了一些畫蛇添足的代碼,結果在業務高峰期一股腦把數據加載到了內存中。 上面這些問題通過分析業務代碼,也不難發現其關聯性。問題如果非常隱蔽,則需要使用 OQL 等語言,對問題一一排查、確認。 可以看到,上手 MAT 工具是有一定門檻的,除了其操作模式,還需要對我們前面介紹的理論知識有深入的理解,比如 GC Roots、各種引用級別等。 在很多場景,MAT 并不僅僅用于內存泄漏的排查。由于我們能夠看到內存上的具體數據,在排查一些難度非常高的 bug 時,MAT 也有用武之地。比如,因為某些臟數據,引起了程序的執行異常,此時,想要找到它們,不要忘了 MAT 這個老朋友。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看