运行时删除顶级容器
不幸的是,看起来这个最近closures的问题还没有得到很好的理解。 这是典型的输出:
run: Trying to Remove JDialog Remove Cycle Done :-) Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 1 ----------------------------------------------------------- Trying to Remove JDialog Remove Cycle Done :-) Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 2 ----------------------------------------------------------- Trying to Remove JDialog Remove Cycle Done :-) Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 3 ----------------------------------------------------------- Trying to Remove JDialog Remove Cycle Done :-) Checking if still exists any of TopLayoutContainers JFrame JDialog *** End of Cycle Without Success, Exit App *** BUILD SUCCESSFUL (total time: 13 seconds)
我会试着再次问这个问题:我怎样才能在运行时启动首先打开的顶级Container
,并帮助为我closures一个Swing NightMares?
import java.awt.*; import java.awt.event.WindowEvent; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.*; public class RemoveDialogOnRuntime extends JFrame { private static final long serialVersionUID = 1L; private int contID = 1; private boolean runProcess; private int top = 20; private int left = 20; private int maxLoop = 0; public RemoveDialogOnRuntime() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setPreferredSize(new Dimension(300, 300)); setTitle("Remove Dialog On Runtime"); setLocation(150, 150); pack(); setVisible(true); Point loc = this.getLocation(); top += loc.x; left += loc.y; AddNewDialog(); } private void AddNewDialog() { DialogRemove firstDialog = new DialogRemove(); remWins(); } private void remWins() { runProcess = true; Thread th = new Thread(new RemTask()); th.setDaemon(false); th.setPriority(Thread.MIN_PRIORITY); th.start(); } private class RemTask implements Runnable { @Override public void run() { while (runProcess) { Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JDialog) { System.out.println(" Trying to Remove JDialog"); wins[i].setVisible(false); wins[i].dispose(); WindowEvent windowClosing = new WindowEvent(wins[i], WindowEvent.WINDOW_CLOSING); wins[i].dispatchEvent(windowClosing); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing); Runtime runtime = Runtime.getRuntime(); runtime.gc(); runtime.runFinalization(); } try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(RemoveDialogOnRuntime.class.getName()).log(Level.SEVERE, null, ex); } } wins = null; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { System.out.println(" Remove Cycle Done :-)"); Runtime.getRuntime().runFinalization(); Runtime.getRuntime().gc(); runProcess = false; } }); } pastRemWins(); } } private void pastRemWins() { System.out.println(" Checking if still exists any of TopLayoutContainers"); Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JFrame) { System.out.println("JFrame"); wins[i].setVisible(true); } else if (wins[i] instanceof JDialog) { System.out.println("JDialog"); wins[i].setVisible(true); } } if (wins.length > 1) { wins = null; maxLoop++; if (maxLoop <= 3) { System.out.println(" Will Try Remove Dialog again, CycleNo. " + maxLoop); System.out.println(" -----------------------------------------------------------"); remWins(); } else { System.out.println(" -----------------------------------------------------------"); System.out.println("*** End of Cycle Without Success, Exit App ***"); closeMe(); } } } private void closeMe() { EventQueue.invokeLater(new Runnable() { @Override public void run() { System.exit(0); } }); } private class DialogRemove extends JDialog { private static final long serialVersionUID = 1L; DialogRemove(final Frame parent) { super(parent, "SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } private DialogRemove() { setTitle("SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } } public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { @Override public void run() { RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime(); } }); } }
调用dispose()
允许主机平台回收重量级对等体消耗的内存,但是直到在 EventQueue
上处理WINDOW_CLOSING
事件之后才能这样做。 即使如此, gc()
是一个build议。
附录:另一种看到噩梦的方法是通过一个分析器。 用jvisualvm
运行下面的例子,可以看到周期性的集合从来没有完全返回到基线。 我从一个人为的小堆开始,夸大了纵轴。 其他示例如下所示。 当内存非常有限时,我使用了两种方法:
-
紧急:从命令行循环,每次启动一个新的虚拟机。
-
紧急:完全消除重量级组件,运行无头,使用2Dgraphics和轻量级组件在
BufferedImage
组成。
import java.awt.Dimension; import java.awt.EventQueue; import java.awt.event.WindowEvent; import javax.swing.JDialog; /** @see https://stackoverflow.com/questions/6309407 */ public class DialogClose extends JDialog { public DialogClose(int i) { this.setTitle("Dialog " + String.valueOf(i)); this.setPreferredSize(new Dimension(320, 200)); } private void display() { this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); this.pack(); this.setLocationRelativeTo(null); this.setVisible(true); passSomeTime(); this.setVisible(false); this.dispatchEvent(new WindowEvent( this, WindowEvent.WINDOW_CLOSING)); this.dispose(); passSomeTime(); } private void passSomeTime() { try { Thread.sleep(100); } catch (InterruptedException ie) { ie.printStackTrace(System.err); } } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { int count = 0; while (true) { new DialogClose(count++).display(); } } }); } }
我已经完全重写了你的例子:
- 我已经简化了什么是不需要的(
setLocation()
,未使用的构造函数…) - 我已经删除了触发WINDOW_CLOSING事件的代码(无用)
- 我已经删除了代码,将所有窗口重置为可见(这将防止他们的GC)
- 我已经使用
javax.swing.Timer
而不是一个Thread
来处理对话框 - 我已经使用了一个
Thread
来强制GC(在EDT中不是一个好主意) - 我修改了最后的成功标准来检查
Window.getWindows()
是2 (不是1 ),因为在Swing中,如果你打开一个没有父对象的对话框,那么将会创build一个特殊的不可见框架来使用它作为父对象所有无主的对话框),一旦创build,该框架不能被删除。
结果摘录如下:
import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class RemoveDialogOnRuntime extends JFrame { private static final long serialVersionUID = 1L; private boolean runProcess; private int maxLoop = 0; private Timer timer; public RemoveDialogOnRuntime() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setPreferredSize(new Dimension(300, 300)); setTitle("Remove Dialog On Runtime"); setLocation(150, 150); pack(); setVisible(true); addNewDialog(); } private void addNewDialog() { DialogRemove firstDialog = new DialogRemove(); remWins(); } private void remWins() { runProcess = true; timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (runProcess) { for (Window win: Window.getWindows()) { if (win instanceof JDialog) { System.out.println(" Trying to Remove JDialog"); win.dispose(); } } System.out.println(" Remove Cycle Done :-)"); runProcess = false; new Thread() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } Runtime.getRuntime().gc(); } }.start(); } else { pastRemWins(); runProcess = true; } } }); timer.setRepeats(true); timer.start(); } private void pastRemWins() { System.out.println(" Checking if still exists any of TopLayoutContainers"); Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JFrame) { System.out.println("JFrame"); } else if (wins[i] instanceof JDialog) { System.out.println("JDialog"); } else { System.out.println(wins[i].getClass().getSimpleName()); } } // We must expect 2 windows here: this (RemoveDialogOnRuntime) and the parent of all parentless dialogs if (wins.length > 2) { wins = null; maxLoop++; if (maxLoop <= 3) { System.out.println(" Will Try Remove Dialog again, CycleNo. " + maxLoop); System.out.println(" -----------------------------------------------------------"); remWins(); } else { System.out.println(" -----------------------------------------------------------"); System.out.println("*** End of Cycle Without Success, Exit App ***"); closeMe(); } } else { timer.stop(); } } private void closeMe() { System.exit(0); } private class DialogRemove extends JDialog { private static final long serialVersionUID = 1L; private DialogRemove() { setTitle("SecondDialog"); setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } } public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { @Override public void run() { RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime(); } }); } }
重要的结论是:
- 您无法删除由Swing创build的不可见框架,作为所有无主对话框的父项
- 你必须强制一个GC的处置对话框从
Window.getWindows()
(这看起来像一个错误,但我认为原因是Swing保持一个WeakReference
的所有窗口,这个WeakReference
不会被释放,直到GC已经发生。
希望这给你一个清晰和完整的答案你的问题。
意图吹走对EDT的所有疑虑,并确认垃圾更新的build议,然后输出到控制台
run: 7163 KB used before GC Trying to Remove JDialog Remove Cycle Done :-) 405 KB used after GC Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 1 ----------------------------------------------------------- 3274 KB used before GC Trying to Remove JDialog Remove Cycle Done :-) 403 KB used after GC Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 2 ----------------------------------------------------------- 3271 KB used before GC Trying to Remove JDialog Remove Cycle Done :-) 406 KB used after GC Checking if still exists any of TopLayoutContainers JFrame JDialog Will Try Remove Dialog again, CycleNo. 3 ----------------------------------------------------------- 3275 KB used before GC Trying to Remove JDialog Remove Cycle Done :-) 403 KB used after GC Checking if still exists any of TopLayoutContainers JFrame JDialog ----------------------------------------------------------- *** End of Cycle Without Success, Exit App *** BUILD SUCCESSFUL (total time: 26 seconds)
来自代码
import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.WindowEvent; import javax.swing.*; public class RemoveDialogOnRuntime extends JFrame { private static final long serialVersionUID = 1L; private int contID = 1; private boolean runProcess; private int top = 20; private int left = 20; private int maxLoop = 0; private javax.swing.Timer timer = null; public RemoveDialogOnRuntime() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setPreferredSize(new Dimension(300, 300)); setTitle("Remove Dialog On Runtime"); setLocation(150, 150); pack(); setVisible(true); Point loc = this.getLocation(); top += loc.x; left += loc.y; AddNewDialog(); } private void AddNewDialog() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { DialogRemove firstDialog = new DialogRemove(); startAA(); } }); } private void startAA() { timer = new javax.swing.Timer(5000, updateAA()); timer.setRepeats(false); timer.start(); } public Action updateAA() { return new AbstractAction("text load action") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { timer.stop(); if (SwingUtilities.isEventDispatchThread()) { Runnable doRun = new Runnable() { @Override public void run() { remWins(); } }; SwingUtilities.invokeLater(doRun); } else { Runnable doRun = new Runnable() { @Override public void run() { remWins(); } }; SwingUtilities.invokeLater(doRun); } } }; } private void remWins() { Runtime runtime = Runtime.getRuntime(); long total = runtime.totalMemory(); long free = runtime.freeMemory(); long max = runtime.maxMemory(); long used = total - free; System.out.println(Math.round(used / 1e3) + " KB used before GC"); Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JDialog) { System.out.println(" Trying to Remove JDialog"); wins[i].setVisible(false); wins[i].dispose(); WindowEvent windowClosing = new WindowEvent(wins[i], WindowEvent.WINDOW_CLOSING); wins[i].dispatchEvent(windowClosing); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing); runtime = Runtime.getRuntime(); runtime.gc(); runtime.runFinalization(); } } wins = null; System.out.println(" Remove Cycle Done :-)"); runtime.runFinalization(); runtime.gc(); runtime = Runtime.getRuntime(); total = runtime.totalMemory(); free = runtime.freeMemory(); max = runtime.maxMemory(); used = total - free; System.out.println(Math.round(used / 1e3) + " KB used after GC"); startOO(); } private void startOO() { timer = new javax.swing.Timer(5000, updateOO()); timer.setRepeats(false); timer.start(); } public Action updateOO() { return new AbstractAction("text load action") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { timer.stop(); timer.stop(); if (SwingUtilities.isEventDispatchThread()) { Runnable doRun = new Runnable() {//really contraproductive just dealayed @Override public void run() { pastRemWins(); } }; SwingUtilities.invokeLater(doRun); } else { Runnable doRun = new Runnable() { @Override public void run() { pastRemWins(); } }; SwingUtilities.invokeLater(doRun); } } }; } private void pastRemWins() { System.out.println(" Checking if still exists any of TopLayoutContainers"); Window[] wins = Window.getWindows(); for (int i = 0; i < wins.length; i++) { if (wins[i] instanceof JFrame) { System.out.println("JFrame"); wins[i].setVisible(true); } else if (wins[i] instanceof JDialog) { System.out.println("JDialog"); wins[i].setVisible(true); } } if (wins.length > 1) { wins = null; maxLoop++; if (maxLoop <= 3) { System.out.println(" Will Try Remove Dialog again, CycleNo. " + maxLoop); System.out.println(" -----------------------------------------------------------"); remWins(); } else { System.out.println(" -----------------------------------------------------------"); System.out.println("*** End of Cycle Without Success, Exit App ***"); closeMe(); } } startAA(); } private void closeMe() { EventQueue.invokeLater(new Runnable() { @Override public void run() { System.exit(0); } }); } private class DialogRemove extends JDialog { private static final long serialVersionUID = 1L; DialogRemove(final Frame parent) { super(parent, "SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } private DialogRemove() { setTitle("SecondDialog " + (contID++)); setLocation(top, left); top += 20; left += 20; setPreferredSize(new Dimension(200, 200)); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setModalityType(Dialog.ModalityType.MODELESS); pack(); setVisible(true); } } public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { @Override public void run() { RemoveDialogOnRuntime superConstructor = new RemoveDialogOnRuntime(); } }); } }
我不确定你的问题是关于“垃圾回收”还是关于如何识别可见的对话框。
垃圾收集完成后,您无法控制。 调用gc()方法只是一个build议。
如果你想忽略“disposed”对话框,那么你可以使用isDisplayable()方法来检查它的状态。
随着下面的程序,我得到了一些有趣的结果。 我做的第一个改变是在对话框中添加一些组件,以便为每个对话使用更多的资源,这将增加资源被垃圾收集的机会。
在我的机器上,我发现如果我
a)创build5个对话框
b)closures对话框
c)创build5个对话框
然后前五个似乎是垃圾收集。
但是,如果我创build5,然后closures然后创build1,然后closures,它似乎不工作。
底线是你不能依靠什么时候垃圾收集完成,所以我build议你使用isDisplayable()方法来确定如何做你的处理。 “显示对话框”button将此方法用作显示输出的一部分。
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class DialogSSCCE extends JPanel { public static int count; public DialogSSCCE() { JButton display = new JButton("Display Dialogs"); display.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(); System.out.println("Display Dialogs"); for (Window window: Window.getWindows()) { if (window instanceof JDialog) { JDialog dialog = (JDialog)window; System.out.println("\t" + dialog.getTitle() + " " + dialog.isDisplayable()); } } } }); add( display ); JButton open = new JButton("Create Dialog"); open.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(); System.out.println("Create Dialog"); JDialog dialog = new JDialog(); dialog.getContentPane().setLayout(null); for (int i = 0; i < 200; i++) { dialog.add( new JTextField("some text") ); } dialog.setTitle("Dialog " + count++); dialog.setLocation(count * 25, count * 25); dialog.setVisible(true); System.out.println("\tCreated " + dialog.getTitle()); } }); add( open ); JButton close = new JButton("Close Dialogs"); close.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println(); System.out.println("Close Dialogs"); for (Window window: Window.getWindows()) { if (window instanceof JDialog) { JDialog dialog = (JDialog)window; System.out.println("\tClosing " + dialog.getTitle()); dialog.dispose(); } } Runtime.getRuntime().gc(); } }); add( close ); } private static void createAndShowUI() { JFrame frame = new JFrame("DialogSSCCE"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add( new DialogSSCCE() ); frame.pack(); frame.setLocationRelativeTo( null ); frame.setVisible( true ); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { createAndShowUI(); } }); } }
在AppContext
定义了一个超时,最后才会释放一些资源。 这被设置为5秒左右。 因此,如果您等待另外五秒钟,上下文将把(最后一个)引用放置到对话框中。
wins = null; Thread.sleep(5000);