3

我目前正在调查我们的一个应用程序中的内存泄漏。经过进一步调查,我想出了两个简单的 java swing 应用程序的测试,它们闲置了将近 14 个小时。这两个应用程序都包含 30 个 JButton。

第一个应用程序对其动作侦听器使用强引用:

jButton1.addActionListener(new java.awt.event.ActionListener() {
     public void actionPerformed(java.awt.event.ActionEvent evt) {
           jButton1ActionPerformed(evt);
     }
});

第二个应用程序对其动作侦听器使用弱引用:

jButton1.addActionListener(new WeakActionListener(new MyActionListener(), this.jButton1))

这是 WeakActionListener 的实现:

public class WeakActionListener implements ActionListener {

    private WeakReference weakListenerReference;
    private Object source;


    public WeakActionListener(ActionListener listener, Object source) {
        this.weakListenerReference = new WeakReference(listener);
        this.source = source;
    }

    public void actionPerformed(ActionEvent actionEvent) {
        ActionListener actionListener = (ActionListener) this.weakListenerReference.get();
        if(actionListener == null) {
            this.removeListener();
        } else {
            actionListener.actionPerformed(actionEvent);
        }
    }

    private void removeListener() {
        try {
            Method method = source.getClass().getMethod("removeActionListener", new Class[] {ActionListener.class});
            method.invoke(source, new Object[] {this});
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }

}

我使用 JConsole 对这两个应用程序进行了 14 小时的概要分析。我只是让他们在那个时间范围内闲置。它表明,无论是使用弱引用还是强引用,这两个应用程序的内存堆消耗都会随着时间的推移而增加。

我的问题是,这是 Java Swing API 中的错误吗?解决这种内存泄漏的其他选择是什么?

提前致谢!

4

1 回答 1

2

JDK实际上包含很多内存泄漏,但是您所描述的情况不是其中之一。内存消耗很严重,因为即使应用程序处于空闲状态,操作系统也不会 - 它有时会向应用程序发送输入事件。处理这些事件需要一些内存分配,因此您会看到越来越多的堆。而且它可能没有被收集,因为它不需要 - 应用程序在堆中有大量可用内存,因此它不会太频繁地 GC。

此外,您在 Java 中使用错误的方法进行内存泄漏分析。正确的将是以下内容:

  1. 您创建了一个简单的应用程序,其中包含您想要分析内存泄漏的功能。
  2. 您执行所有步骤,之后您认为内存应该为 GC 做好准备。对于与 GUI 相关的应用程序,我建议添加一个调用来((SunToolkit)Toolkit.getDefaultToolkit()).realSync()处理所有异步调用和事件。这是一个内部 API,因此不应该在真实的应用程序上使用它,但它对于此类实验性应用程序非常有用。
  3. 之后,您尝试分配一些大对象以导致 OutOfMemoryError。您可以确定在抛出 OOME 之前,Java 会收集所有空闲对象。在 OOME 的 catch 块中,您只需退出应用程序System.exit(0);

现在最有趣的部分是:您应该运行应用程序-Xmx20M来为堆和-Xrunhprof:format=b,file=<Path to output file>. 因此,当应用程序完成时,您可以确定所有空闲对象都已被 GC,因为您导致了 OutOfMemory,并且 hprof 将转储所有剩余对象的堆。您可以使用 jhat 或 eclipse 中的一些工具等可用工具之一来分析堆。

于 2013-03-27T09:58:13.090 回答