在上一次【】已经编写了一个能在堆空间出现内存溢出的代码,先来回顾一下:




JAVA visualVM如何分析大对象_编程语言


其中咱们给JVM配置了如下参数:


JAVA visualVM如何分析大对象_java_02


其中还设置了一个当发生内存溢出时来将内存的信息给dump出来,其实就类似于Android中来分析内存也是需要dump内存信息一样,如下:


JAVA visualVM如何分析大对象_android_03


其dump出来的文件在这个目录之下:


JAVA visualVM如何分析大对象_编程语言_04


其实这个dump出来的文件也叫做“转储”文件,那用何工具来分析呢,有很多工具可以分析,这里学习一下之前也介绍的jvisualvm,它是由oracle基于hospot虚拟机力推的一个集大成者的一个功能超级强大的图形化分析工具,它是集成了很多的命令行的工具使得我们在一个GUI上看到不管是正在运行的JVM进程的种种信息,包括线程的信息、元空间的信息,堆空间的信息等等,还可以分析我们dump出来的转储文件,所以咱们先来打开此工具,在命令行中输入:


JAVA visualVM如何分析大对象_python_05


JAVA visualVM如何分析大对象_jvm_06


接下来咱们来打开转储文件:


JAVA visualVM如何分析大对象_编程语言_07


JAVA visualVM如何分析大对象_jvm_08


JAVA visualVM如何分析大对象_python_09


JAVA visualVM如何分析大对象_编程语言_10


接下来就详细来分析一下该转储文件:


JAVA visualVM如何分析大对象_java_11


堆转储上的线程:

  
"main" prio=5 tid=1 RUNNABLE
    at 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[]#301
    at 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#64646
    at com.jvm.memory.MyTest1.main(MyTest1.java:11)
       Local Variable: java.util.ArrayList#7

  
"Finalizer" daemon prio=8 tid=3 WAITING
    at 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#26
    at 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 WAITING
    at 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异常如果在真实项目中出现了肯定就是大问题了,对于它其实都比较熟悉了,还是来瞅一下它的官方对它的解释:


JAVA visualVM如何分析大对象_jvm_12


其中该异常的继承体系如下 :


JAVA visualVM如何分析大对象_java_13


JAVA visualVM如何分析大对象_编程语言_14


JAVA visualVM如何分析大对象_java_15


好,再回到jvisualvm,目前咱们是在这个视图上看到的信息:


JAVA visualVM如何分析大对象_编程语言_16


接着来切换到类瞅一下:


JAVA visualVM如何分析大对象_android_17


好,接下来再来看另外一个视图:


JAVA visualVM如何分析大对象_python_18


也就是说需要在类视图中来选择要查看的实例数,所以咱们回到类视图来操作一下:


JAVA visualVM如何分析大对象_编程语言_19


此时就可以自动跳到实例数这个视图了,如下:


JAVA visualVM如何分析大对象_jvm_20


而在实例数视图左侧看到有个500个实例的提示:


JAVA visualVM如何分析大对象_jvm_21


貌似跟我们在类似图看到个数不一样啊:


JAVA visualVM如何分析大对象_java_22


其实不是500个,还有其它木有展开而已,如下:


JAVA visualVM如何分析大对象_jvm_23


接着点击一下其中的实例,在右侧可以看到具体的字段信息,如下:


JAVA visualVM如何分析大对象_编程语言_24


然后还能看到类加载器相关的信息,很显然我们自己创业的类是由应用类加载器所加载的:


JAVA visualVM如何分析大对象_java_25


接着还可以看一下它的父加载器,很显然是由扩展类加载器加载的:


JAVA visualVM如何分析大对象_android_26


而它的父加载器很显然就是根类加载器,也就是null嘛:


JAVA visualVM如何分析大对象_android_27


进一步对咱们之前学习的类加载器相关的知识进行巩固,好,再看最后一下视图:


JAVA visualVM如何分析大对象_android_28


接下来咱们再来改造一下我们的程序:


JAVA visualVM如何分析大对象_android_29


咱们先来看一下gc()方法的官方解释:


JAVA visualVM如何分析大对象_编程语言_30


它最终调用的是System类中的gc(),如下:


JAVA visualVM如何分析大对象_编程语言_31


咱们再来瞅下它的gc()注释:


JAVA visualVM如何分析大对象_编程语言_32


从上面的解释也就说明了为啥在实际项目中不鼓励手动去调这个gc()方法,咱们这样做目的是为了学习研究仅此而已,当手动调用了gc()之后程序内部发生了啥变化呢?这里还是借用jvisualvm来查看下,首先找到咱们运行的进程:


JAVA visualVM如何分析大对象_jvm_33


然后双击打开它:


JAVA visualVM如何分析大对象_jvm_34


JAVA visualVM如何分析大对象_jvm_35


也有几个视图,咱们一个个来瞅下,先来看下监视:


JAVA visualVM如何分析大对象_python_36


可见是实时对进程的情况进行监视的,咱们细看一下:


JAVA visualVM如何分析大对象_android_37


JAVA visualVM如何分析大对象_android_38


JAVA visualVM如何分析大对象_android_39


接下来再切一个视图:


JAVA visualVM如何分析大对象_编程语言_40


然后再切另外一个视图:


JAVA visualVM如何分析大对象_jvm_41


最后一个视图是用来分析性能的:


JAVA visualVM如何分析大对象_android_42


大致了解下既可,这里我们从jvisualvm的分析中可以发现我们的程序在堆中的使用基本是维持在2MB左右的,如下:


JAVA visualVM如何分析大对象_编程语言_43


那。。如果我们手动将JVM的堆内存由目前的5MB改成1MB呢,看我们程序虽说主动调用了gc()看是否还会有溢出出现,试一下:


JAVA visualVM如何分析大对象_java_44


JAVA visualVM如何分析大对象_jvm_45


其实很容易理解,我们设置的堆内存是在1MB,而实际我们程序堆内存会在2MB左右,当然会溢出啦。