2008年9月16日星期二

JVM_019:JRockit Memory Leak Detector 使用说明

JRockit Memory Leak Detector 的趋势分析器可以发现非常慢的泄漏,它显示详细的Heap统计信息(包括泄漏对象的引用类型和实例,分配点),并可快速追溯泄漏的原因。
内存泄漏检测程序使用先进的图形表示技术,简化了复杂信息的浏览和理解。

那么,如何使用JRockit Memory Leak Detector 呢?
说明:本实验使用的JRockit Mission Control的版本为4.0.0。

1. 使用前的环境准备
(1)设置JAVA_HOME为JRockit所在目录。
(2)设置PATH=%JAVA_HOME%\bin;%PATH%
(3)运行 java -version 确认使用的JVM的确是 JRockit,以及具体版本。
(4)运行 java -Xpausetarget=30ms -Xverbose:gc DemoLeak。
(5)运行 启动 JAVA_HOME\bin\jrmc.exe。
(6)找到本地连接的Java程序DemoLeak,右键选择 Start Memleak。

2. 观察
JRockit Memory Leak Detector 有6个tab,分别是:

  • 趋势
  • 类型图形
  • 类型树
  • 实例图形
  • 实例树
  • 分配
下面,我们就具体看一看如何适用这几个Tab。
趋势分析显示了Heap中最常见的对象类型、 对象类型的增长率、对象类型的实例数、所占内存大小 。趋势分析的运行时间越长,趋势就越可靠。
各个类型是按照增长速率的顺序排列的,我们最关注的当然是增长率最高的类型——这些类型很可能就是引起内存泄漏的类型。
可以看出,上图中增长率排在前三位的是:DemoLeak$DemoObject 、Hashtable$Entry 、Hashtable$Entry[] 。
到底是哪个对象类型引起了内存泄漏呢?仔细观察,我们发现DemoLeak$DemoObject 与Hashtable$Entry 实例数大致相当,因此我们可以推测Hashtable$Entry 中存放的就是DemoObject。 

为了证实推测,右键点击DemoLeak$DemoObject ,选择“添加到类型图形”,看看有哪些类型指向了DemoObject。
在类型图形Tab中,我们展开指向DemoObject的所有节点,发现只有Hashtable$Entry——我们的猜测是对的!


继续展开指向Hashtable$Entry 的所有节点,发现只有Hashtable$Entry[]。右键Hashtable$Entry[],选择“列出最大数组”——因为最大的数组最可能引起内存泄露!

右键最大数组,选择“添加到实例图形”,发现有一个HashTable指向该数组。继续展开全部节点——有两个DemoThread 对象引用了该HashTable。
说明:本实验过程中断了一次,故最大数组新显示为12287,就是前面所指的393215。

右键其中一个DemoThread,选择“检查实例”。发现第一个参数是table,且指向该有问题的数组。
分析至此,我们基本找到问题的源头了——就是DemoThread.java中的table属性。剩下的工作就是分析该table属性,在哪个地方被不正确地使用了,最终导致了这个可疑的最大数组的产生。


3.  DemoLeak.java
import java.util.Hashtable;
import java.util.List;
import java.util.ArrayList;

/*
 * Copyright (c) 2007 by BEA Systems, Inc. All Rights Reserved.
 *
 * Created on 2003-dec-04
 */

/**
 * Simple MemLeak demo
 */
public class DemoLeak {
    private static class DemoObject {
        private long position;
        long myField1 = 1;
        long myField2 = 2;

        public DemoObject(int pos) {
            position = pos;
        }

        public int hashCode() {
            return (int)position;
        }

        public boolean equals(Object o) {
            return (o instanceof DemoObject) && (o.hashCode() == position);
        }
    }

    private static class AllocThread extends Thread {
        public void run() {
            while (true) {
                List junkList = new ArrayList();
                for (int i = 0; i < 1000; i++) {
                    junkList.add(new Object());
                    for (int j = 0; j < 10; j++)
                        Thread.yield(); // Keep busy yielding for a little while...
                }
            }
        }
    }

    private static class DemoThread extends Thread {
        private Hashtable table;

        DemoThread(Hashtable h) {
            table = h;
        }

        public void run() {
            int total = 0;
            while (true) {
                for (int i = 0; i <= 100; i++)
                    put(total + i);

                for (int i = 0; i < 100; i++)
                    remove(total + i);
                total += 101;

                for (int i = 0; i < 10; i++)
                    Thread.yield(); // Keep busy yielding for a little while...
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }

        private void put(int n) {
            table.put(new DemoObject(n), "foo");
        }

        private String remove(int n) {
            return (String)table.remove(new DemoObject(n));
        }
    }

    public static void main(String[] args) {
        Hashtable h = new Hashtable();
        Thread[] threads;

        if (args.length != 1) {
            threads = new Thread[2];
        } else {
            threads = new Thread[Integer.parseInt(args[0])];
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new DemoThread(h);
            threads[i].start();
        }
        new AllocThread().start();
    }
}

没有评论: