哪些对象可以作为根对象呢?使用eclipse的MAT(memory analyzer)可以进行分析。这个工具比jvisual更加专业,可以找到内存泄漏。

运行如下代码。

/**
 * 演示GC Roots
 */
public class Demo2_2 {

    public static void main(String[] args) throws InterruptedException, IOException {
        List<Object> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        System.out.println(1);
        System.in.read();

        list1 = null;
        System.out.println(2);
        System.in.read();
        System.out.println("end...");
    }
} 

使用命令jps查看当前运行代码的进程为17332。

PS F:\资料 解密JVM\代码\jvm> jps
13472 Launcher
3296 Jps
12868 RemoteMavenServer36
17332 Demo2_2
11180

在list回收前、后分别使用jamp抓取目标进程内存的快照,转储为二进制文件,并设置live参数在抓取快照前主动触发垃圾回收。回收前的操作命令如下。

jmap -dump:format=b,live,file=gcRootDemo.bin 17332

使用elipse的MAT工具可以来分析内存泄漏问题,官网下载安装MAT,https://www.eclipse.org/mat/downloads.php。

使用MAT工具,菜单栏file->open dump file打开刚才抓取的快照文件。打开文件时报错Invalid HPROF file header,其中一个原因为人工改变了文件的编码格式,重新抓取并不要改变编码格式。将两个文件都打开以方便对照。如下图,查看文件的GC Roots。

图片说明

可以看到GC Root的具体情况,被分为了4类。
图片说明

System Class是程序运行所必须的核心类。

图片说明

第二类是执行本地方法时操作系统所引用的Java对象的类。

图片说明

Busy Monitor是指正在加锁的对象,如果这对象被回收了,则锁无法被释放,故不会被回收。

图片说明

最后是活动着的线程中,局部变量所引用的对象不能被当成垃圾回收。比如下图中的ArrayList其实就是对应代码中list1被垃圾回收前所指对象,在list对象回收后的抓取的内存快照gcRootDemo2.bin中该对象不存在了。

图片说明