首页 > 解决方案 > JDialog 关闭后留在 Swings 组件树中

问题描述

我有一个对话框视图,它使用 PropertyListener 将事件传播给调用者。我们使用 swing 组件内置的 PropertyListeners。现在,我们打开这些对话框之一,然后用户通过系统中的“X”按钮将其关闭。稍后我们调用以下代码段:

  for ( final Window frame : Frame.getFrames() )
  {
    updateAllUIs( frame );
  }

  private static void updateAllUIs( final Window frame )
  {
    for ( final Window childWindow : frame.getOwnedWindows() )
    {
      updateAllUIs( childWindow );
    }
    SwingUtilities.updateComponentTreeUI( frame );
  }

显然,这甚至可以到达早已关闭的对话框。是否dispose被调用也没有区别。是否有一种可靠的方法来确保 Swing 不再包含对不再使用的对话框的任何内部引用?虽然我们应该清除我们的听众,但这仍然不应该发生。或者我在这里误解了什么?

重现问题的最小示例:

package dialogleak;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Window;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class DialogLeak
{
  public static void main( String[] args )
  {
    JFrame mainFrame = new JFrame();

    JButton dialogButton = new JButton( "Dialog" );
    dialogButton.addActionListener( __ ->
    {
      JDialog dialog = new JDialog( mainFrame );
      dialog.setModal( true );
      dialog.add( new JTextField( "Hello world" ) );
      dialog.getRootPane().addPropertyChangeListener( new PropertyChangeListener()
      {
        @Override
        public void propertyChange( PropertyChangeEvent evt )
        {
          //Shouldn't be visible when dialog is invisible.
          System.out.println( "Fired: " + evt.getPropertyName() + "; Dialog visible: " + dialog.isVisible() );
        }
      } );
      dialog.setSize( 200, 100 );
      dialog.setLocationRelativeTo( mainFrame );
      //Doesn't make a difference, feel free to try.
      //dialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
      dialog.setVisible( true );
    } );

    JButton updateUIButton = new JButton( "Update UI" );
    updateUIButton.addActionListener( __ ->
    {
      for ( Frame frame : Frame.getFrames() )
      {
        updateUIRecursively( frame );
      }
    } );

    JPanel panel = new JPanel( new BorderLayout() );
    mainFrame.add( panel );
    panel.add( dialogButton, BorderLayout.NORTH );
    panel.add( updateUIButton, BorderLayout.SOUTH );
    mainFrame.setSize( 400, 300 );
    mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    mainFrame.setLocationRelativeTo( null );
    mainFrame.setVisible( true );
  }

  private static void updateUIRecursively( Window window )
  {
    SwingUtilities.updateComponentTreeUI( window );
    for ( final Window childWindow : window.getOwnedWindows() )
    {
      updateUIRecursively( childWindow );
    }
  }
}

标签: javaswingmemory-leakswindowlifecycle

解决方案


推荐阅读