*当我尝试在JFrame中创建按钮时,我现在遇到一个非常奇怪的java GC问题,当我单击按钮时,它会显示需要处理并显示一些图像的JDialog,需要近200M记忆。但问题是当我关闭对话框并重新打开它时,有时它会导致java.lang.OutOfMemoryError。 (不是每次都)
尝试解决问题,我简化了这个问题并做了一些实验,让我更加困惑。
我在“实验”中使用的代码如下所示。 当我单击一个框架中的一个按钮时,我为整数数组分配160M内存,并显示一个对话框,但如果我关闭对话框并重新打开它,则会出现OutOfMemoryError。我调整代码和结果是:
如果我在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代表你有一个对v的引用,root是一个线程工作堆栈中的东西,或者是原生资源,它是无用的,有资格被java的GC收集。
现在问题是当我单击按钮并尝试创建一个Integer数组时,我上次创建的Integer数组肯定有资格被java的GC收集。那么为什么它会导致错误。
为这样一个长篇描述道歉......我在提问时没有太多策略,所以只是试图说明问题。
此外,我用来启动jvm的参数是“java -Xmx256m”
答案 0 :(得分:3)
您在new int[40000000]
仍然保留对最后tmp
的引用之前分配int[40000000]
。
像tmp = new int[40000]
这样的表达式中的操作顺序是:
new int[40000]
tmp
所以在 1。 tmp
仍然持有对它的最后一个值的引用。
尝试做:
tmp = null;
tmp = new int[40000000];
答案 1 :(得分: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。