空闲简单的Java Swing应用程序内存泄漏

时间:2013-03-26 01:48:52

标签: java swing memory-leaks actionlistener weak-references

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

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

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中的一个错误吗?解决此类内存泄漏的其他替代方法是什么?

提前致谢!

1 个答案:

答案 0 :(得分:2)

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

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

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

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