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 に答える