以下演示应用程序使用JInternalFrame(MDI接口)创建JFrame。内部框架具有JTree,具有JTree模型。为了模拟大型模型,将一个10MB缓冲区与JTree模型相关联。当内部框架关闭(处置)时,JTree及其模型将永远不会被垃圾收集。
jvisualvm显示了原因 - 一些静态Swing类字段将保留对JTree的引用。与其他Swing内存泄漏不同,此处不使用事件处理程序。
这是一个错误吗?是否有一个干净的解决方案收集已处理的内部框架,其树和模型(除了使用弱引用之外的解决方法,使JTree模型中的数据无效)?
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class Test extends javax.swing.JFrame {
public Test() {
javax.swing.JDesktopPane jDesktopPane = new javax.swing.JDesktopPane();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setContentPane(jDesktopPane);
InternalTreeFrame f = new InternalTreeFrame();
jDesktopPane.add(f);
f.show();
pack();
setBounds(10, 10, 400, 300);
}
public class InternalTreeFrame extends javax.swing.JInternalFrame {
public InternalTreeFrame() {
javax.swing.JScrollPane jScrollPane = new javax.swing.JScrollPane();
javax.swing.JTree jTree = new javax.swing.JTree();
jScrollPane.setViewportView(jTree);
jTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(new LargeObject("big root"))));
setContentPane(jScrollPane);
setClosable(true);
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
pack();
setBounds(10, 10, 300, 200);
}
}
// 10MB helper object, easy to spot in visual vm heap dump
public class LargeObject {
public LargeObject(String name) {
this.name = name;
buff = new byte[1024*1024*10];
}
@Override
public String toString() {
return name;
}
private final String name;
private final byte[] buff;
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Test().setVisible(true);
}
});
}
}
答案 0 :(得分:1)
不确定这是否是一个错误,但解决方案是在(内部)框架的windowClosing()事件中将空模型设置为JTree
答案 1 :(得分:1)
JDK中有一个已知的bug,关于最后一个JINternalFrame没有在dispose
上正确地进行垃圾回收。这是因为JDesktopPane
维护了framesCache
,其中包含对JInternalFrames的引用。当最后一个JInternalFrame关闭时,不会刷新此缓存。
解决方法是通过调用JDesktopPane.selectFrame
强制缓存重新加载,如下所示:
f.addInternalFrameListener(new InternalFrameAdapter() {
public void internalFrameClosed(InternalFrameEvent e) {
jDesktopPane.selectFrame(true);
}
});
尝试一下,一旦JInternalFrame关闭,你会看到回收内存。