【问题标题】:Java Swing JTree is not garbage collectedJava Swing JTree 没有被垃圾回收
【发布时间】:2011-05-29 21:50:56
【问题描述】:

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

【问题讨论】:

    标签: java swing memory garbage-collection memory-leaks


    【解决方案1】:

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

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

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

    试试看,一旦 JInternalFrame 关闭,你会看到内存回收。

    【讨论】:

    • 谢谢,我已经试过了。首先,我修改了测试用例以创建 10 个内部框架。关闭所有这些,我看到两个未回收的 TreeModel 实例。一个来自 JDesktopPane 的 framesCache,另一个来自 CompositionAreaHandler 中的静态字段。使用缓存重新加载方法,frameCache 引用的实例被回收,但另一个仍然存在。
    【解决方案2】:

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

    【讨论】:

    • 谢谢!是的,这种解决方法实际上是我目前正在使用的。我已经实现了一个自定义 dispose() 方法,它调用 super.dispose() 并删除对树模型中大型数据项的引用。我刚刚问过自己,是否有更清洁的方法来解决这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 2015-05-15
    • 1970-01-01
    • 1970-01-01
    • 2014-03-09
    • 1970-01-01
    相关资源
    最近更新 更多