2013-10-25 14 views
7

* Tôi đang gặp phải một vấn đề java GC rất lạ khi tôi cố gắng tạo một nút trong JFrame và khi tôi nhấp vào nút, nó hiển thị một JDialog cần phải xử lý và hiển thị một số hình ảnh và cần gần 200 triệu bộ nhớ. Nhưng vấn đề là khi tôi đóng hộp thoại và mở lại nó, đôi khi nó gây ra java.lang.OutOfMemoryError. (không phải mọi lần)Thu gom rác java trong hộp thoại

Cố gắng giải quyết vấn đề, tôi đơn giản hóa vấn đề này và thực hiện một số thử nghiệm, khiến tôi bối rối hơn.

Mã tôi đã sử dụng trong "thử nghiệm" của mình được hiển thị bên dưới. Khi tôi bấm vào một nút trong một khung, tôi cấp phát bộ nhớ 160M cho một mảng số nguyên và hiển thị hộp thoại, Nhưng nếu tôi đóng hộp thoại và mở lại, thì OutOfMemoryError sẽ xuất hiện. Tôi điều chỉnh mã và kết quả là:

  1. Nếu tôi không tạo hộp thoại và hiển thị nó, không có vấn đề về bộ nhớ.
  2. Nếu tôi thêm một cửa sổCloseListener gọi System.gc() vào hộp thoại, không có vấn đề về bộ nhớ.
  3. Nếu tôi gọi System.gc() trong phương thức run(), vấn đề bộ nhớ sẽ hiển thị.

    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); 
         } 
        }); 
        } 
    } 
    

Tôi đã đọc về một bài viết rất nhiều trong đó mô tả cách làm việc GC java của. Tôi nghĩ rằng nếu java cố gắng phân bổ một số không gian trong heap và nó không có đủ không gian trống, java sẽ làm gc, và nếu một đối tượng không thể được truy cập từ gốc gc thông qua "đồ thị GC", trong đó một cạnh từ u để v đại diện cho u có một tham chiếu đến v, root là một cái gì đó trong một ngăn xếp thread làm việc, hoặc nguồn gốc, Nó vô dụng và đủ điều kiện để được thu thập bởi GC của java.

Bây giờ vấn đề là Khi tôi nhấp vào nút và cố gắng tạo mảng Integer, mảng Integer tôi tạo lần trước chắc chắn đủ điều kiện để được thu thập bởi GC của java. Vậy tại sao nó gây ra lỗi.

Xin lỗi cho mô tả dài như vậy ... Tôi không có nhiều chiến thuật trong việc đặt vấn đề, vì vậy chỉ cần cố gắng làm rõ.

Bên cạnh đó, Các thông số tôi sử dụng để bắt đầu JVM là “java -Xmx256m”

+0

1+ cho những nỗ lực của bạn khi gỡ lỗi, để tạo ra một sscce và dành nỗ lực vào câu hỏi của bạn. Tôi không có thời gian trong công việc để chạy hoặc gỡ lỗi chương trình của bạn, nhưng tôi tự hỏi liệu bạn có gặp sự cố với sự kiên trì các tham chiếu mềm do người nghe đưa ra hay không. –

+0

Điều gì sẽ xảy ra nếu bạn thực hiện hộp thoại * phương thức *, và sau đó trong mã gọi hộp thoại, hủy bỏ hộp thoại sau khi sử dụng bằng cách gọi 'dispose()' trên đó. –

Trả lời

3

Bạn đang phân bổ new int[40000000] trước khi tmp vẫn giữ tham chiếu đến cuối int[40000000].
Trình tự hoạt động trong một biểu thức như tmp = new int[40000] là:

  1. new int[40000]
  2. Gán tham chiếu đến mảng để tmp

Vì vậy, trong 1.tmp vẫn giữ tham chiếu đến đó là giá trị cuối cùng.

Hãy thử làm:

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

Nó có thể giúp chạy 'System.gc()' sau khi đặt 'tmp' thành' null' khi thấy chính xác những gì bạn muốn JVM làm tại thời điểm đó. Lưu ý rằng JVM không yêu cầu phải chạy một bộ sưu tập đầy đủ rác với 'System.gc()', nhưng nó có thể sẽ trong trường hợp này. – Chill

+0

Xin cảm ơn hai bạn của suggenstion, It's helpful.But Trên thực tế, Mỗi lần tôi tạo một đối tượng TestController mới và sử dụng mảng tmp [] của nó, vì vậy chúng không có cùng tham chiếu. Ngoài ra, tôi cố gắng đặt tmp = null trước tmp = new int [xxx], nó không hoạt động, vẫn gây ra java.lang.OutOfMemoryError – ryanaaa

2

Hãy thử điều này:

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); 
     } 
     }); 
    } 
} 

nơi bạn dọn dẹp cả thoại và dữ liệu của nó trong phương pháp finish(). Các hộp thoại một lần nữa nên được phương thức cho việc này để làm việc, nếu không bạn sẽ cần một WindowListener.


Bạn nhà nước trong bình luận:

Nhưng bạn sẽ nói cho tôi biết những gì sai trái trong mã của tôi? và ý nghĩa của "là phương thức". Tôi đã đọc api của phương thức setModal của Dialog trong java doc. nó có nghĩa là "cho dù khối hộp thoại đầu vào cho các cửa sổ khác khi được hiển thị", có vẻ không giống như bạn đã giới thiệu.

Một hộp thoại phương thức thực tế là khối chặn đầu vào từ cửa sổ gọi và thực tế sẽ đóng băng luồng mã từ mã gọi ngay khi hộp thoại hiển thị. Sau đó, mã sẽ tiếp tục khi hộp thoại không còn hiển thị nữa.

Không có giải pháp kỳ diệu nào cho vấn đề của bạn với hộp thoại là phương thức, nhưng nó cho phép chúng tôi biết chính xác khi nào hộp thoại không còn nhìn thấy nữa - mã sẽ tiếp tục từ nơi hộp thoại được hiển thị. để gọi mã dọn dẹp tại thời điểm này. Ở đây tôi gọi phương thức finish().

Nếu bạn không muốn hộp thoại trở thành phương thức, bạn cần có WindowListener và lắng nghe hộp thoại đang đóng, sau đó thực hiện xong phương thức kết thúc của bạn.

Tất cả mã của tôi là đảm bảo rằng mảng int có sẵn cho GC'ing trước khi bạn tạo một mảng int mới.

+0

Cảm ơn sự giúp đỡ của bạn! mã của bạn hoạt động, Nhưng bạn sẽ cho tôi biết những gì sai trong mã của tôi? và ý nghĩa của "là phương thức". Tôi đã đọc api của phương thức setModal của Dialog trong java doc. nó có nghĩa là "cho dù khối hộp thoại đầu vào cho các cửa sổ khác khi được hiển thị", có vẻ không giống như bạn đã giới thiệu. – ryanaaa

+0

lại cùng một bài hát, re_use, re_use hoặc xóa +1 – mKorbel

+0

giống với Hình ảnh, Vectơ, những đối tượng đó sẽ không bao giờ được hoàn thành, sau đó GC không có bất kỳ sở thích nào, bởi vì nó là cha mẹ, Cấp cao nhất havent Container finalize() trong API, sau đó nó con sẽ không bao giờ được GC'ed, nhưng referrence là null, – mKorbel