内存泄漏与摆动拖放

tom*_*tom 7 java swing memory-leaks drag-and-drop

我有一个接受顶级文件丢弃的JFrame.然而,在发生丢弃之后,对框架的引用将无限期地保留在某些Swing内部类中.我相信处理框架应该释放所有资源,所以我做错了什么?

import java.awt.datatransfer.DataFlavor;
import java.io.File;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.TransferHandler;

public class DnDLeakTester extends JFrame {
    public static void main(String[] args) {
        new DnDLeakTester();

        //Prevent main from returning or the jvm will exit
        while (true) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {

            }
        }
    }
    public DnDLeakTester() {
        super("I'm leaky");

        add(new JLabel("Drop stuff here"));

        setTransferHandler(new TransferHandler() {
            @Override
            public boolean canImport(final TransferSupport support) {
                return (support.isDrop() && support
                        .isDataFlavorSupported(DataFlavor.javaFileListFlavor));
            }

            @Override
            public boolean importData(final TransferSupport support) {
                if (!canImport(support)) {
                    return false;
                }

                try {
                    final List<File> files = (List<File>) 
                            support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);

                    for (final File f : files) {
                        System.out.println(f.getName());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return true;
            }
        });

        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        pack();
        setVisible(true);
    }
}
Run Code Online (Sandbox Code Playgroud)

要重现,请运行代码并删除框架上的一些文件.关闭框架,使其处理掉.

为了验证泄漏,我使用JConsole进行堆转储,并使用Eclipse Memory Analysis工具对其进行分析.它显示sun.awt.AppContext通过其hashmap持有对帧的引用.看起来TransferSupport有问题.

GC根路径的图像http://img402.imageshack.us/img402/4444/dndleak.png

我究竟做错了什么?我应该要求DnD支持代码以某种方式清理自己吗?

我正在运行JDK 1.6 update 19.

mdm*_*dma 4

虽然 DropHandler 没有从静态 AppContext 映射中删除,但这并不是真正的根本原因,而只是链中的原因之一。(drop 处理程序旨在成为一个单例,并且在 AppContext 类卸载之前不会被清除,而实际上永远不会被清除。)单例 DropHandler 的使用是设计使然。

泄漏的真正原因是 DropHandler 设置了一个 TransferSupport 实例,该实例被每个 DnD 操作重用,并在 DnD 操作期间为其提供对 DnD 中涉及的组件的引用。问题是当DnD 完成时它没有清除引用。如果 DropHandlerTransferSupport.setDNDVariables(null,null)在 DnD 退出时调用,那么问题就会消失。这也是最合乎逻辑的解决方案,因为仅在 DnD 进行时才需要引用组件。其他方法(例如清除 AppContext 映射)是在规避设计,而不是修复一个小疏忽。

但即使我们解决了这个问题,框架仍然不会被收集。不幸的是,似乎还有另一个问题:当我注释掉所有 DnD 相关代码,减少到只是一个简单的 JFrame 时,这也没有被收集。复鞣参考位于javax.swing.BufferStrategyPaintManager. 有一个关于此的错误报告,但尚未修复。

因此,如果我们修复 DnD,我们就会遇到重新绘制的另一个保留问题。幸运的是,所有这些错误都只存在于一帧中(希望是同一帧!),所以它并没有想象的那么糟糕。框架被处置,因此本机资源被释放,并且所有内容都可以被删除,从而允许释放内容,从而降低内存泄漏的严重性。

所以,最后回答你的问题,你没有做错任何事,你只是给了 JDK 中的一些错误一点时间!

更新:重绘管理器错误有一个快速修复 - 添加

-Dswing.bufferPerWindow=false
Run Code Online (Sandbox Code Playgroud)

给jvm启动选项避免了这个bug。随着此错误的消除,发布 DnD 错误的修复程序是有意义的:

要解决 DnD 问题,您可以在 importData() 末尾添加对此方法的调用。

            private void cancelDnD(TransferSupport support)
            {
                /*TransferSupport.setDNDVariables(Component component, DropTargetEvent event) 
                Call setDNDVariables(null, null) to free the component.
*/
                try
                {
                    Method m = support.getClass().getDeclaredMethod("setDNDVariables", new Class[] { Component.class, DropTargetEvent.class });
                    m.setAccessible(true);
                    m.invoke(support, null, null);
                    System.out.println("cancelledDnd");
                }
                catch (Exception e)
                {
                }
            }
Run Code Online (Sandbox Code Playgroud)