实战jvisualvm
在上一次【https://www.cnblogs.com/webor2006/p/10629889.html】已經編寫了一個能在堆空間出現內存溢出的代碼,先來回顧一下:
其中咱們給JVM配置了如下參數:
其中還設置了一個當發生內存溢出時來將內存的信息給dump出來,其實就類似于Android中來分析內存也是需要dump內存信息一樣,如下:
其dump出來的文件在這個目錄之下:
其實這個dump出來的文件也叫做“轉儲”文件,那用何工具來分析呢,有很多工具可以分析,這里學習一下之前也介紹的jvisualvm,它是由oracle基于hospot虛擬機力推的一個集大成者的一個功能超級強大的圖形化分析工具,它是集成了很多的命令行的工具使得我們在一個GUI上看到不管是正在運行的JVM進程的種種信息,包括線程的信息、元空間的信息,堆空間的信息等等,還可以分析我們dump出來的轉儲文件,所以咱們先來打開此工具,在命令行中輸入:
接下來咱們來打開轉儲文件:
接下來就詳細來分析一下該轉儲文件:
堆轉儲上的線程:"main" prio=5 tid=1 RUNNABLEat java.lang.OutOfMemoryError.<init>(OutOfMemoryError.java:48)at java.util.Arrays.copyOf(Arrays.java:3210)Local Variable: class java.lang.Object[]at java.util.Arrays.copyOf(Arrays.java:3181)Local Variable: java.lang.Object[]#301at java.util.ArrayList.grow(ArrayList.java:265)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)at java.util.ArrayList.add(ArrayList.java:462)Local Variable: com.jvm.memory.MyTest1#64646at com.jvm.memory.MyTest1.main(MyTest1.java:11)Local Variable: java.util.ArrayList#7"Finalizer" daemon prio=8 tid=3 WAITINGat java.lang.Object.wait(Native Method)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)Local Variable: java.lang.ref.ReferenceQueue#26at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:212)Local Variable: java.lang.System$2#1"Signal Dispatcher" daemon prio=9 tid=4 RUNNABLE"Reference Handler" daemon prio=10 tid=2 WAITINGat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)可以清晰的看到線程的具體異常信息,其中OutOfMemoryError異常如果在真實項目中出現了肯定就是大問題了,對于它其實都比較熟悉了,還是來瞅一下它的官方對它的解釋:
其中該異常的繼承體系如下 :
好,再回到jvisualvm,目前咱們是在這個視圖上看到的信息:
接著來切換到類瞅一下:
好,接下來再來看另外一個視圖:
也就是說需要在類視圖中來選擇要查看的實例數,所以咱們回到類視圖來操作一下:
此時就可以自動跳到實例數這個視圖了,如下:
而在實例數視圖左側看到有個500個實例的提示:
貌似跟我們在類似圖看到個數不一樣啊:
其實不是500個,還有其它木有展開而已,如下:
接著點擊一下其中的實例,在右側可以看到具體的字段信息,如下:
然后還能看到類加載器相關的信息,很顯然我們自己創業的類是由應用類加載器所加載的:
接著還可以看一下它的父加載器,很顯然是由擴展類加載器加載的:
而它的父加載器很顯然就是根類加載器,也就是null嘛:
進一步對咱們之前學習的類加載器相關的知識進行鞏固,好,再看最后一下視圖:
接下來咱們再來改造一下我們的程序:
咱們先來看一下gc()方法的官方解釋:
它最終調用的是System類中的gc(),如下:
咱們再來瞅下它的gc()注釋:
從上面的解釋也就說明了為啥在實際項目中不鼓勵手動去調這個gc()方法,咱們這樣做目的是為了學習研究僅此而已,當手動調用了gc()之后程序內部發生了啥變化呢?這里還是借用jvisualvm來查看下,首先找到咱們運行的進程:
然后雙擊打開它:
也有幾個視圖,咱們一個個來瞅下,先來看下監視:
可見是實時對進程的情況進行監視的,咱們細看一下:
接下來再切一個視圖:
然后再切另外一個視圖:
最后一個視圖是用來分析性能的:
大致了解下既可,這里我們從jvisualvm的分析中可以發現我們的程序在堆中的使用基本是維持在2MB左右的,如下:
那。。如果我們手動將JVM的堆內存由目前的5MB改成1MB呢,看我們程序雖說主動調用了gc()看是否還會有溢出出現,試一下:
其實很容易理解,我們設置的堆內存是在1MB,而實際我們程序堆內存會在2MB左右,當然會溢出啦。
轉載于:https://www.cnblogs.com/webor2006/p/10646305.html
總結
以上是生活随笔為你收集整理的实战jvisualvm的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何创建基本的高级队列之一:创建发送方代
- 下一篇: (转载)最黑的黑客米特尼克:多次耍FBI