3

在调用 JDialog.dispose 来处理 JDialog 时,我观察到 OS 和 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 错误报告(我没有运气找到一个)?

任何建议的解决方法?

4

2 回答 2

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,但总比没有好,应该对您有所帮助。

于 2013-11-13T22:30:06.460 回答
0

我使用以下代码来尝试最小化内存泄漏。仍然会有垃圾收集器未收集的资源,但是作为 JFrame 或 JDialog 子级的所有 Swing 组件都将被垃圾收集。更短的标题(或无标题)可用于使占用空间更小。我保留了一个有意义的标题,以便在必要时可以更轻松地跟踪分析器中的内容。使用此代码的应用程序的内存占用对于长时间运行和大量窗口打开和关闭操作来说非常小。如果没有它,内存将在某些用户正在使用的某些重量级窗口上进行几十次打开和关闭操作而耗尽,同时让应用程序连续打开数天。

        protected void disposeAndEmptyOnClose(Component c) {
            if ( c instanceof JFrame ) {
                JFrame frame = (JFrame) c;
                if (!frame.getClass().isAssignableFrom(JFrame.class)) {
                    LOG.warn("potential memory leak. Cannot guarantee memory is freed after frame is disposed because" +
                        " JFrame has been subclassed to " + frame.getClass().getName());
                }
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosed(WindowEvent e) {
                        frame.removeAll();
                        frame.setContentPane(new JPanel());
                        frame.setJMenuBar(null);
                        frame.removeWindowListener(this);
                        frame.setTitle("disposed and emptied: "+frame.getTitle());
                    }
                });
            } else if ( c instanceof JDialog ) {
                JDialog dialog = (JDialog)c;
                if (!dialog.getClass().isAssignableFrom(JDialog.class)) {
                    LOG.warn("potential memory leak. Cannot guarantee memory is freed after dialog is disposed " +
                        "because JDialog has been subclassed to " + dialog.getClass().getName());
                }
                dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                dialog.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosed(WindowEvent e) {
                        dialog.removeAll();
                        dialog.setContentPane(new JPanel());
                        dialog.removeWindowListener(this);
                        dialog.setTitle("disposed and emptied: "+dialog.getTitle());
                    }
                });
            } else {
                LOG.warn("disposeAndEmptyOnClose not supported for " + c.getClass().getSimpleName());
            }
        }
于 2021-08-18T15:44:54.413 回答