public class MapLeaker {
    public ExecutorService exec = Executors.newFixedThreadPool(5);
    public Map taskStatus
        = Collections.synchronizedMap(new HashMap());
    private Random random = new Random();
    private enum TaskStatus { NOT_STARTED, STARTED, FINISHED };
    private class Task implements Runnable {
        private int[] numbers = new int[random.nextInt(200)];
        public void run() {
            int[] temp = new int[random.nextInt(10000)];
            taskStatus.put(this, TaskStatus.STARTED);
            doSomeWork();
            taskStatus.put(this, TaskStatus.FINISHED);
        }
    }
    public Task newTask() {
        Task t = new Task();
        taskStatus.put(t, TaskStatus.NOT_STARTED);
        exec.execute(t);
        return t;
    }
}
  图 1 显示 MapLeaker GC 之后应用程序堆大小随着时间的变化图。上升趋势是存在内存泄漏的警示信号。(在真实的应用程序中,坡度不会这么大,但是在收集了足够长时间的 GC 数据后,上升趋势通常会表现得很明显。)

  图 1. 持续上升的内存使用趋势

  确信有了内存泄漏后,下一步是找出哪种对象造成了这个问题。所有内存分析器都可以生成按照对象类进行分解的堆快照。有一些很好的商业堆分析工具,但是找出内存泄漏不一定要花钱买这些工具 —— 内置的 hprof 工具也可完成这项工作。要使用 hprof 并让它跟踪内存使用,需要以 -Xrunhprof:heap=sites 选项调用 JVM。

  清单 3 显示分解了应用程序内存使用的 hprof 输出的相关部分。(hprof 工具在应用程序退出时,或者用 kill -3 或在 Windows 中按 Ctrl+Break 时生成使用分解。)注意两次快照相比,Map.Entry、Task 和 int[] 对象有了显著增加。

  清单 3. HPROF 输出,显示 Map.Entry 和 Task 对象的增加


SITES BEGIN (ordered by live bytes) Fri Oct 28 16:30:48 2005
          percent          live          alloc'ed  stack class
 rank   self  accum     bytes objs     bytes  objs trace name
    1 70.13% 70.13%   5694888 13909   5694888 13909 300305 int[]
    2 18.27% 88.40%   1483976   68 278273632 13908 300321 int[]
    3  4.11% 92.51%    333816 13909    333816 13909 300310 java.util.HashMap$Entry
    4  2.74% 95.25%    222544 13909    222544 13909 300304 com.quiotix.dummy.MapLeaker$Task
    5  2.42% 97.67%    196640    2    262192    11 300325 java.util.HashMap$Entry[]
    6  0.66% 98.33%     53680 3355    222464 13904 300324 java.util.concurrent.LinkedBlockingQueue$Node
SITES END
SITES BEGIN (ordered by live bytes) Fri Oct 28 16:31:32 2005
          percent          live          alloc'ed  stack class
 rank   self  accum     bytes objs     bytes  objs trace name
    1 77.07% 77.07%  41176024 100020  41176024 100020 301069 int[]
    2 12.98% 90.05%   6933768  359 2001885688 100020 301093 int[]
    3  4.49% 94.55%   2400480 100020   2400480 100020 301082 java.util.HashMap$Entry
    4  3.00% 97.54%   1600320 100020   1600320 100020 301068 com.quiotix.dummy.MapLeaker$Task
    5  1.96% 99.50%   1048592    1   2097248    14 301104 java.util.HashMap$Entry[]
    6  0.05% 99.55%     25936 1621   1600240 100015 301101 java.util.concurrent.LinkedBlockingQueue$Node
SITES END