Mac OS上Java 7中JDialog.dispose存在内存泄漏问题。

23 浏览
0 Comments

Mac OS上Java 7中JDialog.dispose存在内存泄漏问题。

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

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

我不认为这是由于任何weak references,因为我在一个经历过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错误报告(我找不到任何一篇)?

有什么建议的解决方法吗?

admin 更改状态以发布 2023年5月22日
0
0 Comments

我使用以下代码来尝试最小化内存泄漏。垃圾回收器仍将有资源未被回收,但是所有作为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());
            }
        }

0
0 Comments

我也遇到了同样的问题,并且通过在我的窗口上覆盖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,但好过没有,并且应该能帮到你。

0