Mac OS Java 7 JDialog.dispose内存泄漏

时间:2013-11-05 04:19:45

标签: java macos swing memory-leaks awt

在调用JDialog.dispose来处理JDialog时,我观察到操作系统和Java版本之间存在一些不一致的行为(也适用于JFrame)。

下面的简单示例应用程序可用于演示此问题。如果您运行它并对应用程序进行概要分析,您会注意到通过单击“新建对话框”创建并随后关闭的任何JDialog实例都不会被垃圾收集,因为它们仍然被sun.lwawt.macosx.CPlatformWindow的实例引用,从而导致内存泄漏应用程序。

我不相信这是由于任何弱引用,因为我在经历过OutOfMemoryError的环境中观察到这个问题,所以我希望任何可能被垃圾收集的东西都是在那一点。

在以下环境中出现问题:

  • Mac OS X 10.9:Java 1.7.0_5
  • Mac OS X 10.9:Java 1.7.0_45

在以下环境中出现问题:

  • Mac OS X 10.9:Java 1.6.0_65
  • Windows 7:Java 1.7.0_45

在这些环境中,JDialog实例被迅速收集,并且(显然)在JProfiler中不再可见。

注意:使用DISPOSE_ON_CLOSE或在示例中注释掉手动处理关闭时会出现问题。

import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.*;

public class Testing extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JDialog parent = new JDialog((Frame)null, "Parent", false);

                JButton add = new JButton("New Dialog");
                add.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        final JDialog child = new JDialog(parent, "Child", false);
                        // child.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
                        child.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);                       
                        child.setSize(100, 100);

                        //child.addWindowListener(new WindowAdapter() {
                        //    @Override
                        //    public void windowClosing(WindowEvent e) {
                        //        child.setVisible(false);
                        //        child.dispose();
                        //    }
                        //});
                        child.setVisible(true);
                    }
                });

                parent.add(add);
                parent.pack();
                parent.setVisible(true);
            }
        });
    }
}

我做错了吗?

我的预期行为是否不正确?

如果没有,是否有人可以向我指出一个涵盖此问题的Java错误报告(我找不到运气)?

任何建议的解决方法?

1 个答案:

答案 0 :(得分:2)

我看到同样的事情并且能够通过覆盖我窗口上的dispose方法来释放窗口,如下所示:

@SuppressWarnings("deprecation")
@Override
public void dispose()
{
    ComponentPeer peer = getPeer();

    super.dispose();

    if (null != peer)
    {
        try
        {
            Class<?> peerClass = Class.forName("sun.lwawt.LWComponentPeer");

            Field targetField = peerClass.getDeclaredField("target");
            targetField.setAccessible(true);
            targetField.set(peer, null);

            Field windowField = peer.getClass().getDeclaredField("platformWindow");
            windowField.setAccessible(true);
            Object platformWindow = windowField.get(peer);

            targetField = platformWindow.getClass().getDeclaredField("target");
            targetField.setAccessible(true);
            targetField.set(platformWindow, null);

            Field componentField = peerClass.getDeclaredField("platformComponent");
            componentField.setAccessible(true);
            Object platformComponent = componentField.get(peer);

            targetField = platformComponent.getClass().getDeclaredField("target");
            targetField.setAccessible(true);
            targetField.set(platformComponent, null);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

这并没有发布CPlatformWindow,但它总比没有好,应该帮助你。