Java Swing JTree不会被垃圾回收

3
以下演示应用程序创建一个具有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);
  }
      });
   }
}
2个回答

2

在JDK中已知一个与最后一个JInternalFrame在dispose时没有正确回收的bug。这是因为JDesktopPane维护了一个framesCache,其中保存了对JInternalFrames的引用。当关闭最后一个JInternalFrame时,此缓存不会刷新。

解决方法是通过调用JDesktopPane.selectFrame来强制重新加载缓存,如下所示:

    f.addInternalFrameListener(new InternalFrameAdapter() {
        public void internalFrameClosed(InternalFrameEvent e) {
            jDesktopPane.selectFrame(true);
        }
    });

试一试,当JInternalFrame关闭后,您将看到内存被回收。


谢谢,我已经尝试过了。首先,我修改了测试用例以创建10个内部框架。关闭它们后,我发现有两个TreeModel实例没有被回收。其中一个是从JDesktopPane的framesCache引用的,另一个是从CompositionAreaHandler的静态字段引用的。使用缓存重新加载方法可以回收framesCache引用的实例,但另一个实例仍然存在。 - FreeJack

2

不确定这是否是一个错误,但解决方案是在窗口关闭(内部)框架的windowClosing()事件中将空模型设置到JTree中。


谢谢!是的,这种解决方法实际上是我目前正在使用的。我已经实现了一个自定义的dispose()方法,它调用super.dispose()并且删除树模型中大数据项的引用。我只是在想,是否有更简洁的方法来解决这个问题。 - FreeJack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接