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.
虽然 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)
| 归档时间: |
|
| 查看次数: |
1709 次 |
| 最近记录: |