2013-10-25 44 views
7

*當我嘗試在JFrame中創建按鈕時,我現在遇到了一個非常奇怪的java GC問題,當我單擊該按鈕時,它顯示一個需要處理並顯示一些JDialog圖像並需要近200M內存。但問題是當我關閉對話框並重新打開它時,有時會導致java.lang.OutOfMemoryError。 (不是每一次)在對話框中收集java垃圾

試圖解決這個問題,我簡化了這個問題,並做了一些實驗,使我更加困惑的

我在「實驗」中使用的代碼如下所示。 當我點擊一個框架中的按鈕,我分配160M內存的整數數組,並顯示一個對話框,但是,如果我關閉對話框並重新打開它,出現OutOfMemoryError。我調整的代碼和結果是:

  1. 如果我不創建對話框並顯示它,沒有內存問題。
  2. 如果我添加一個調用System.gc()到對話框的windowsCloseListener,沒有內存問題。
  3. 如果我在run()方法中調用System.gc(),則顯示內存問題。

    public class TestController { 
        int[] tmp; 
    
        class TDialog extends JDialog { 
        public TDialog() { 
         super(); 
         this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 
         // If I uncommment this code, OutOfMemoryError seems to dispear in this situation 
         // But I'm sure it not a acceptable solution 
         /* 
         this.addWindowListener(new WindowAdapter() { 
         public void windowClosing(WindowEvent e) { 
          System.out.println("windowsclose"); 
          TDialog.this.dispose(); 
          System.gc(); 
         } 
         }); 
         */ 
        } 
        } 
    
        TDialog dia; 
    
        public void run() { 
        // If I do System.gc() here, OutOfMemoryError still exist 
        // System.gc(); 
        tmp = new int[40000000]; 
        for (int i = 0; i < tmp.length; i += 10) 
         tmp[i] = new Random().nextInt(); 
    
        dia = new TDialog(); 
        dia.setVisible(true); 
        } 
    
        public static void main(String[] args) { 
        EventQueue.invokeLater(new Runnable() { 
         @Override 
         public void run() { 
         final JFrame frame = new JFrame("test"); 
         frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
         frame.setLocationRelativeTo(null); 
         frame.setSize(200, 200); 
    
         JButton button = new JButton("button"); 
         button.addActionListener(new ActionListener() { 
          @Override 
          public void actionPerformed(ActionEvent e) { 
          TestController controller = new TestController(); 
          controller.run(); 
          controller = null; 
          } 
         }); 
    
         frame.add(button); 
         frame.setVisible(true); 
         } 
        }); 
        } 
    } 
    

我讀過有關它描述了Java的GC工作了很多文章。我認爲如果java試圖在堆中分配一些空間並且它沒有足夠的可用空間,那麼java將會執行gc,並且如果一個對象不能通過GC graph從GC根訪問,其中一個邊你代表你有一個對v的引用,root是一個線程工作棧或本地資源中的東西,它沒有用,也沒有資格被java的GC收集。

現在的問題是當我點擊按鈕並嘗試創建一個Integer數組時,我上次創建的Integer數組肯定有資格被java的GC收集。那麼爲什麼它會導致錯誤。

道歉這麼長的描述......我沒有太多的提問策略,所以只是試圖說清楚。

此外,我用來啓動JVM的參數是「的java -Xmx256m」

+0

1+爲您的調試努力,爲創建sscce,並努力到什麼看起來是你的問題。我沒有時間去運行或調試你的程序,但是我不知道你是否遇到了由聽衆帶來的軟引用問題。 –

+0

如果你創建了對話框* modal *,然後在調用對話框的代碼中,通過調用'dispose()'來處理對話框。 –

回答

3

tmp仍佔據參考最後int[40000000]之前你分配new int[40000000]
操作在像tmp = new int[40000]表達的順序是:

  1. new int[40000]
  2. 分配參考陣列tmp

所以在1.tmp中仍保持參考這是最後的價值。

嘗試這樣做:

tmp = null; 
tmp = new int[40000000]; 
+0

將'tmp'設置爲'null'後,運行'System.gc()'可能會有所幫助,因爲這正是您希望JVM在此時執行的操作。請注意,JVM不需要使用'System.gc()'運行完整的垃圾回收,但在這種情況下可能會發生。 – Chill

+0

謝謝你們兩個人的建議,這很有幫助。但實際上,我每次創建一個新的TestController對象並使用它的tmp []數組,因此它們沒有相同的引用。另外,我嘗試在tmp = new int [xxx]之前放置tmp = null,但它不起作用,仍然會導致java.lang.OutOfMemoryError – ryanaaa

2

試試這個:

import java.awt.*; 
import java.awt.event.*; 
import java.util.Random; 
import javax.swing.*; 

public class TestController { 
    private JFrame frame; 
    int[] tmp; 

    public TestController(JFrame frame) { 
     this.frame = frame; 
    } 

    public void finish() { 
     if (dia != null) { 
     dia.dispose(); 
     } 
     tmp = null; 
    } 

    class TDialog extends JDialog { 
     public TDialog() { 
     super(frame, "Dialog", true); 
     this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 
     } 
    } 

    TDialog dia; 

    public void run() { 
     tmp = new int[40000000]; 
     for (int i = 0; i < tmp.length; i += 10) 
     tmp[i] = new Random().nextInt(); 
     dia = new TDialog(); 
     dia.setVisible(true); 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      final JFrame frame = new JFrame("test"); 
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
      frame.setLocationRelativeTo(null); 
      frame.setSize(200, 200); 
      JButton button = new JButton("button"); 
      button.addActionListener(new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        TestController controller = new TestController(frame); 
        controller.run(); 
        // controller = null; 
        System.out.println("here"); 
        controller.finish(); 
       } 
      }); 
      frame.add(button); 
      frame.setVisible(true); 
     } 
     }); 
    } 
} 

,你清理出兩個對話框,其在finish()方法的數據。對話框應該是模態的,否則你需要一個WindowListener。


幽州的評論:

但是你能告訴我什麼是錯在我的代碼?以及「模態」的含義是什麼。我已經閱讀了java doc中Dialog的setModal方法的api。它意味着「顯示時是否阻止對話框輸入到其他窗口」,看起來與您提到的不同。

模態對話框實際上是阻止來自調用窗口的輸入,並且實際上一旦對話框可見就凍結調用代碼中的代碼流。代碼然後在對話框不再可見時恢復。

對話框本身並不存在不可思議的解決方案,但它允許我們準確知道對話框何時不再可見 - 代碼從對話框設置可見的位置恢復,從而允許我們在這一點上調用清理代碼。這裏我稱之爲finish()方法。

如果您不希望對話框是模態對話框,那麼您需要一個WindowListener並監聽關閉的對話框,然後在那裏完成您的完成方法調用。

我所有的代碼都是在創建一個新的int數組之前確保int數組可用於GC'ing。

+0

感謝您的幫助!你的代碼有效,但是你會告訴我我的代碼中有什麼問題嗎?以及「模態」的含義是什麼。我已經閱讀了java doc中Dialog的setModal方法的api。它意味着「顯示時是否阻止對話框輸入到其他窗口」,看起來與您提到的不同。 – ryanaaa

+0

同樣的歌曲,re_use,re_use或者刪除+1 – mKorbel

+0

與Images,Vectors相同,那些Object永遠不會被定位,那麼GC就沒有任何興趣了,因爲它的父級,頂級容器還沒有finalize()in API,那麼它的孩子永遠不會被GC'ed,但引用爲空, – mKorbel