<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JVM 第四篇:可視化 JVM 故障處理工具

          共 6174字,需瀏覽 13分鐘

           ·

          2020-10-09 15:09

          ?

          本文內(nèi)容過(guò)于硬核,建議有 Java 相關(guān)經(jīng)驗(yàn)人士閱讀。

          ?

          1. 可視化工具

          在 JDK 中為我們提供了大量的 JVM 故障處理工具,都在 JDK 的 bin 目錄下:

          這其中除了大量的命令行工具以外,還為我們提供了更加方便快捷的可視化工具,主要是以下這 4 個(gè):

          • JConsole: 最古老的工具,早在 JDK 5 時(shí)期就已經(jīng)存在的虛擬機(jī)監(jiān)控工具。
          • JHSDB: 名義上在 JDK 9 中才正式提供,但之前已經(jīng)以 sa-jdi.jar 包里面的 HSDB(可視化工具) 和 CLHSDB(命令行工具) 的形式存在了很長(zhǎng)一段時(shí)間。
          • VisualVM: 在 JDK 6 Update 7 中首次發(fā)布,直到 JRockit Mission Control 與 OracleJDK 的融合工作完成之前,它都曾是 Oracle 主力推動(dòng)的多合一故障處理工具,現(xiàn)在它已經(jīng)從 OracleJDK 中分離出來(lái),成為一個(gè)獨(dú)立發(fā)展的開(kāi)源項(xiàng)目。
          • JMC: Java Mission Control ,曾經(jīng)是大名鼎鼎的來(lái)自 BEA 公司的圖形化診斷工具,隨著 BEA 公司被 Oracle 收購(gòu),它便被融合進(jìn) OracleJDK 之中。在 JDK 7 Update 40 時(shí)開(kāi)始隨 JDK 一起發(fā)布,后來(lái) Java SE Advanced 產(chǎn)品線建立, Oracle 明確區(qū)分了 Oracle OpenJDK 和 OracleJDK 的差別, JMC 從 JDK 11 開(kāi)始又被移除出 JDK 。

          2. HSDB

          HSDB(Hotspot Debugger) 是 JDK 自帶的工具,用于查看 JVM 運(yùn)行時(shí)的狀態(tài)。

          使用方式由于在 JDK 9 之前沒(méi)有正式提供,所以也未在 JDK 的 bin 目錄下提供直接可執(zhí)行文件,需要在命令行執(zhí)行命令才能啟動(dòng)。

          首先在命令行中先 cd 至 C:\Program Files\Java\jdk1.8.0_221\lib 目錄,然后執(zhí)行:

          java?-cp?.\sa-jdi.jar?sun.jvm.hotspot.HSDB

          我在執(zhí)行這句命令的時(shí)候報(bào)了個(gè)錯(cuò):

          Exception?in?thread?"Thread-1"?java.lang.UnsatisfiedLinkError:?Can't?load?library:?C:\Program?Files\Java\jdk-11.0.4\bin\sawindbg.dll
          ????????at?java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2620)
          ????????at?java.base/java.lang.Runtime.load0(Runtime.java:767)
          ????????at?java.base/java.lang.System.load(System.java:1831)
          ????????at?sun.jvm.hotspot.debugger.windbg.WindbgDebuggerLocal.(WindbgDebuggerLocal.java:661)
          ????????at?sun.jvm.hotspot.HotSpotAgent.setupDebuggerWin32(HotSpotAgent.java:567)
          ????????at?sun.jvm.hotspot.HotSpotAgent.setupDebugger(HotSpotAgent.java:335)
          ????????at?sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:304)
          ????????at?sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
          ????????at?sun.jvm.hotspot.HSDB.attach(HSDB.java:1184)
          ????????at?sun.jvm.hotspot.HSDB.access$1700(HSDB.java:53)
          ????????at?sun.jvm.hotspot.HSDB$25$1.run(HSDB.java:456)
          ????????at?sun.jvm.hotspot.utilities.WorkerThread$MainLoop.run(WorkerThread.java:66)
          ????????at?java.base/java.lang.Thread.run(Thread.java:834)

          含義是有一個(gè) sawindbg.dll 在 jdk 的目錄下找不到,因?yàn)槲冶镜赜卸鄠€(gè) jdk ,配置環(huán)境變量的是 jdk 11 ,我在 jdk 8 的 jre 的 bin 目錄下找到了這個(gè)文件,直接 copy 到 jdk 11 的bin 目錄下解決此問(wèn)題。

          執(zhí)行完成后會(huì)打開(kāi)這么個(gè)界面:

          接下來(lái),我們寫(xiě)一小段測(cè)試代碼:

          public?abstract?class?A?{
          ????public?void?printMe()?{
          ????????System.out.println("I?am?A?class");
          ????}
          ????public?abstract?void?sayHello();
          }

          public?class?B?extends?A?{
          ????@Override
          ????public?void?sayHello()?{
          ????????System.out.println("I?am?B?class");
          ????}
          }

          public?class?HSDB_Test?{
          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????A?obj?=?new?B();
          ????????//?無(wú)意義,單純用作卡住主線程,防止線程結(jié)束
          ????????System.in.read();
          ????????System.out.println(obj);
          ????}
          }

          首先在命令行中使用命令 jps -l 查看 Java 進(jìn)程的 pid :

          PS?C:\Users\inwsy>?jps?-l
          1648?com.geekdigging.lesson04.jvmtools.HSDB_Test
          8704
          9280?org.jetbrains.jps.cmdline.Launcher
          14724?jdk.jcmd/sun.tools.jps.Jps
          3220?org/netbeans/Main
          15144
          20572?org.jetbrains.jps.cmdline.Launcher
          7548?sun.jvm.hotspot.HSDB

          可以看到,我們剛寫(xiě)的測(cè)試方法的 pid 是 1648 ,在 HSDB 中點(diǎn)擊 File > Attach to Hotspot process :

          第一個(gè)看到的就是當(dāng)前進(jìn)程中的線程:

          到這里,我們的準(zhǔn)備工作就已經(jīng)結(jié)束,接著我們使用 Tools > Class Browser 找到對(duì)象 B 的內(nèi)存地址:

          圖中紅框框起來(lái)的是我自己寫(xiě)的三個(gè)類,可以看到我這里 B 的內(nèi)存地址是 0x00000007c0060c18

          接下來(lái),使用 Tools > Inspector 查看這個(gè)對(duì)象的詳細(xì)信息:

          vtable 是虛表方法,這里我們看到 class B 有 7 個(gè)虛表方法,因?yàn)樗械膶?duì)象都繼承自 Object ,所以 B 繼承了 Object 的 5 個(gè)方法,然后還繼承了 A 的一個(gè)方法,自己重寫(xiě)了一個(gè)方法,總共是 7 個(gè)方法。

          這個(gè)我們可以進(jìn)行一下驗(yàn)證,可以在 Windows > Console 中使用 mem 命令進(jìn)行查看。

          那么我們可以開(kāi)始計(jì)算, vtable 是在 instanceKlass 對(duì)象實(shí)例的尾部,而 instanceKlass 大小在 64 位系統(tǒng)的大小為 0x1B8 ,因此 vtable 的起始地址等于 instanceKlass 的內(nèi)存首地址加上 0x1B8 等于 7C0060DD0 。

          接下來(lái),我們?cè)?Windows > Console 中使用 mem 命令進(jìn)行驗(yàn)證:

          第一列是方法實(shí)際在堆中的內(nèi)存地址,第二列則是內(nèi)存指針地址,我們可以將拿到的內(nèi)存指針地址去 A , B 和 Object 中分別查看,可以看到前 5 行對(duì)應(yīng)的是 Object 的方法,第 6 行對(duì)應(yīng)的是 A 對(duì)象中的方法,第 7 行則對(duì)應(yīng) B 對(duì)象中的方法。

          3. JConsole

          JConsole(Java Monitoring and Management Console) 是一款基于 JMX(Java Manage-ment Extensions) 的可視化監(jiān)視、管理工具。它的主要功能是通過(guò) JMX 的 MBean(Managed Bean) 對(duì)系統(tǒng)進(jìn)行信息收集和參數(shù)動(dòng)態(tài)調(diào)整。

          JConsole 位于 JDK/bin 這個(gè)目錄下,直接雙擊 jconsole.exe 就可以直接啟動(dòng),在啟動(dòng)之后,會(huì)自動(dòng)搜索出當(dāng)前在本機(jī)運(yùn)行的所有虛擬機(jī)進(jìn)程。

          這里可以看到我本機(jī)目前運(yùn)行了一個(gè) JConsole ,一個(gè) idea ,還有一個(gè)啟動(dòng)的 tomcat 的源碼。

          隨便雙擊一個(gè)服務(wù),進(jìn)入主頁(yè)面:

          可以看到主界面里共包括概述、內(nèi)存、線程、類、 VM 摘要、 MBean 六個(gè)頁(yè)簽。

          還是來(lái)個(gè)小示例,我們來(lái)了解下它的監(jiān)控功能。

          public?class?MonitoringTest?{
          ????//?內(nèi)存占位對(duì)象,一個(gè)對(duì)象大約?64KB
          ????static?class?OOMObject?{
          ????????public?byte[]?placeholder?=?new?byte[64?*?1024];
          ????}

          ????public?static?void?fillHeap(int?nums)?throws?InterruptedException?{
          ????????List?list?=?new?ArrayList<>();
          ????????for?(int?i?=?0;?i?????????????Thread.sleep(50);
          ????????????list.add(new?OOMObject());
          ????????}
          ????????System.gc();
          ????}

          ????public?static?void?main(String[]?args)?throws?InterruptedException?{
          ????????fillHeap(1000);
          ????}
          }

          這個(gè)案例是使用大約 64KB/50ms 的速度向 Java 堆中填充數(shù)據(jù),一共填充 1000 次。

          程序執(zhí)行后可以看到,在整個(gè) Java 堆中,曲線一直是平滑向上的。

          切換到內(nèi)存標(biāo)簽頁(yè),查看 Eden 后可以發(fā)現(xiàn),整個(gè) Eden 的圖形是一個(gè)折線:

          再切換到 Gen ,可以看到整個(gè)老年代也是折疊向上的:

          我們已經(jīng)在代碼里加了 System.gc() ,為什么看起來(lái)沒(méi)生效呢?

          因?yàn)?System.gc() 是在 fillHeap() 方法中的,在 GC 的時(shí)候,還在作用域中,想要正?;厥绽夏甏?,需要將 System.gc() 這段代碼轉(zhuǎn)移到 fillHeap() 外面。

          先修改下代碼:

          public?static?void?main(String[]?args)?throws?InterruptedException?{
          ????fillHeap(1000);
          ????System.gc();
          ????//?GC?后停頓?3s?,方便觀察圖像
          ????Thread.sleep(3000);
          }

          可以看到在最后進(jìn)程結(jié)束的時(shí)候, Gen 的柱狀圖已經(jīng)沒(méi)有內(nèi)存占用了,內(nèi)存回收成功。

          3. VisualVM

          VisualVM(All-in-One Java Troubleshooting Tool)是功能最強(qiáng)大的運(yùn)行監(jiān)視和故障處理程序之一,曾經(jīng)在很長(zhǎng)一段時(shí)間內(nèi)是 Oracle 官方主力發(fā)展的虛擬機(jī)故障處理工具。

          VisualVM 同樣在 JDK/bin 這個(gè)目錄下,雙擊 jvisualvm.exe 即可運(yùn)行。在啟動(dòng)之后,直接在左側(cè)會(huì)顯示當(dāng)前在本機(jī)運(yùn)行的所有虛擬機(jī)進(jìn)程。

          VisualVM 基于 NetBeans 平臺(tái)開(kāi)發(fā)工具,所以一開(kāi)始它就具備了通過(guò)插件擴(kuò)展功能的能力,有了插件擴(kuò)展支持, VisualVM 可以做到:

          • 顯示虛擬機(jī)進(jìn)程以及進(jìn)程的配置、環(huán)境信息(jps、jinfo)。
          • 監(jiān)視應(yīng)用程序的處理器、垃圾收集、堆、方法區(qū)以及線程的信息(jstat、jstack)。
          • dump 以及分析堆轉(zhuǎn)儲(chǔ)快照(jmap、jhat)。
          • 方法級(jí)的程序運(yùn)行性能分析,找出被調(diào)用最多、運(yùn)行時(shí)間最長(zhǎng)的方法。
          • 離線程序快照:收集程序的運(yùn)行時(shí)配置、線程 dump 、內(nèi)存 dump 等信息建立一個(gè)快照,可以將快照發(fā)送開(kāi)發(fā)者處進(jìn)行 Bug 反饋。
          • 其他插件帶來(lái)的無(wú)限可能性。

          VisualVM 的插件可以在 工具->插件 中聯(lián)網(wǎng)后直接安裝。

          我這里只安裝了兩個(gè)最常用的,一個(gè)是 GC 監(jiān)控的插件,還有一個(gè)可以動(dòng)態(tài)插入調(diào)試程序的插件。

          我這里使用最常用的開(kāi)發(fā)工具 IDEA 啟動(dòng)過(guò)程演示一下通過(guò) VisualVM 監(jiān)控程序 GC 。

          首先我們啟動(dòng) IDEA ,直到 IDEA 可以正常操作,看下 VisualVM 的 GC 監(jiān)控。

          在主信息面板,可以看到 IDEA 所使用 JVM 的版本信息,可以看到具體的 JAVA_HOME 路徑,還可以看到具體的 JVM 參數(shù),這里可以看到 IDEA 啟動(dòng)時(shí)設(shè)置的默認(rèn)最小堆和最大堆內(nèi)存的設(shè)置分別是 128MB 和 750 MB ,所使用的垃圾回收器則是 CMS 收集器。

          然后點(diǎn)擊 Visual GC,可以看到:

          在啟動(dòng)過(guò)程中, Class 加載消耗了 28s 左右,而 Class 編譯則消耗了 35s 。并且在這個(gè)過(guò)程中, Minor GC 被觸發(fā)了 149 次,消耗只有 713ms ,我們更加關(guān)注的 Full GC 更是一次都沒(méi)有觸發(fā),消耗為 0 。

          因?yàn)?IDEA 默認(rèn)使用的是 CMS 收集器,如果我們換成 G1 收集器會(huì)不會(huì)更快一些呢?

          首先,找到 IDEA 的配置文件,我的 IDEA 是通過(guò) Toolbox 進(jìn)行安裝的,所以我的 IDEA 的配置文件的路徑有點(diǎn)奇怪 D:\Program Files\JetBrains\apps\IDEA-U\ch-0\202.7660.26.vmoptions 。

          先把這個(gè)文件備份到桌面一個(gè),防止改壞了導(dǎo)致 IDEA 不能使用。

          刪掉現(xiàn)有的垃圾回收器配置 -XX:+UseConcMarkSweepGC ,增加 G1 收集器的配置:

          -XX:+UseG1GC

          其余的配置不做修改,直接關(guān)閉 IDEA 重啟,再看下 GC 情況。

          首先先看下主面板,看下我們的 GC 收集器是否已經(jīng)切換成功:

          然后再看下 GC 面板:

          Minor GC 竟然被觸發(fā)了 271 次,而且消耗達(dá)到了 853ms ,好吧,看來(lái)在客戶端還是更適合使用 CMS 做為垃圾回收器。

          我們?cè)傩薷南?-Xmx 這個(gè)配置,將配置的大小縮減為現(xiàn)在的一半,再把 GC 換回原有的 CMS ,看下 Full GC 的情況:

          可以看到, Full GC 整整發(fā)生了 46 次,并且耗時(shí)超過(guò)了 21s ,而且這是 IDEA 的界面上也開(kāi)始彈出警告,警告我們內(nèi)存不足了,需要調(diào)整。

          嚇得我趕緊改回了原有配置,順便把 -Xmx 的大小加到了 1024 ,盡量減少 Full GC 的情況。

          4. Java Mission Control

          JMC 同樣在 JDK/bin 這個(gè)目錄下,雙擊 jmc.exe 即可運(yùn)行。

          打開(kāi)后在 JVM 瀏覽器面板中有兩個(gè)選項(xiàng),一個(gè)是 MBean ,一個(gè)是 JFR 飛行記錄器。

          關(guān)于 MBean 這部分?jǐn)?shù)據(jù),與 JConsole 和 VisualVM 上取到的內(nèi)容是一樣的,只是展示形式上有些差別,就不多說(shuō)了。

          雙擊「飛行記錄器」,將會(huì)出現(xiàn)「飛行記錄器」窗口(如果第一次使用,還會(huì)收到解鎖商業(yè)功能的警告窗)。

          注意:在使用前需要在 JVM 中增加如下兩個(gè)參數(shù),含義是解鎖 JFR 功能的鎖定。

          -XX:+UnlockCommercialFeatures
          -XX:+FlightRecorder

          飛行記錄報(bào)告里包含以下幾類信息:

          • 一般信息:關(guān)于虛擬機(jī)、操作系統(tǒng)和記錄的一般信息。
          • 內(nèi)存:關(guān)于內(nèi)存管理和垃圾收集的信息。
          • 代碼:關(guān)于方法、異常錯(cuò)誤、編譯和類加載的信息。
          • 線程:關(guān)于應(yīng)用程序中線程和鎖的信息。
          • I/O:關(guān)于文件和套接字輸入、輸出的信息。
          • 系統(tǒng):關(guān)于正在運(yùn)行Java虛擬機(jī)的系統(tǒng)、進(jìn)程和環(huán)境變量的信息。
          • 事件:關(guān)于記錄中的事件類型的信息,可以根據(jù)線程或堆棧跟蹤,按照日志或圖形的格式查看。

          5. 小結(jié)

          這 4 款可視化工具看下來(lái),個(gè)人感覺(jué)還是最后一個(gè) JMC 對(duì)使用者來(lái)講最友好, MBean 數(shù)據(jù)源展示了大量的當(dāng)前 JVM 的信息,而且全都以圖表的形式進(jìn)行了展現(xiàn),更加給力還是它的 JFR 功能,可以記錄一段時(shí)間內(nèi)所有的操作,并且以圖表的形式進(jìn)行展現(xiàn),對(duì)我們分析問(wèn)題時(shí)候的幫助無(wú)疑是巨大的。

          當(dāng)然,喜歡用哪款工具完全是個(gè)人喜好,比如 VisualVM 也很強(qiáng)大,可能它本身的功能沒(méi)那么強(qiáng),但是它可以安裝插件,完全根據(jù)需要進(jìn)行插件的安裝,這個(gè)玩法非常 DIY ,總的算下來(lái),我還是喜歡使用 VisualVM 更多一些。

          參考

          《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐_周志明》

          https://www.cnblogs.com/alinainai/p/11070923.html




          感謝閱讀



          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产男女操逼网站 | 日本一道本视频一二三 | 色婷婷五月天亚洲中文字幕 | 99视频免费看 | 中日韩欧美视频操逼 |