2013-11-05 34 views
2

我在调用JDialog.dispose来处置JDialog(也出现在JFrame中)时观察到OS和Java版本之间的某些不一致的行为。Mac OS Java 7 JDialog.dispose内存泄漏

下面的简单示例应用程序可用于演示此问题。如果运行它并对应用程序进行配置,您将注意到,通过单击“New Dialog”创建并随后关闭的任何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报告,涵盖了这个(我没有找到幸运的运气)?

任何建议的解决方法?

+0

检查一个相关示例[here](http://stackoverflow.com/a/6310284/230513)。 – trashgod

+0

@trashgod谢谢,我在调查过程中看到了这一点。然而,我没有看到它是如何直接在这里应用的,因为在以前的Java版本和不同的操作系统中,我看到不同的行为给予完全相同的代码。 –

+0

可能是延迟或错误,但也请参阅[答案](http://stackoverflow.com/a/2486200/230513)。 – trashgod

回答

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但它是优于没有什么,应该帮助你。