2013-11-05 62 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但它是優於沒有什麼,應該幫助你。