Retire el contenedor de nivel superior en tiempo de ejecución

Desafortunadamente, parece que esta pregunta recientemente cerrada no fue bien entendida. Aquí está el resultado típico:

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) 

Trataré de hacer esta pregunta nuevamente: ¿cómo puedo aprovechar el tiempo de ejecución del primer Container nivel superior abierto y ayudarme a cerrar uno de 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  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(); } }); } } 

Invocar dispose() permite que la plataforma de host recupere la memoria consumida por el igual de peso pesado, pero no puede hacerlo hasta después de que el evento WINDOW_CLOSING se procese en EventQueue . Incluso entonces, gc() es una sugerencia.

Adición: Otra forma de ver la pesadilla es a través de un generador de perfiles. Ejecutando el ejemplo a continuación con jvisualvm , uno puede ver que la colección periódica nunca regresa a la línea base. He exagerado el eje vertical comenzando con un montón artificialmente pequeño. Ejemplos adicionales se muestran aquí . Cuando la memoria es muy limitada, he usado dos enfoques:

  • Emergente: realiza un bucle desde la línea de comando, comenzando una nueva máquina virtual cada vez.

  • Urgente: elimine por completo el componente de peso pesado, ejecute sin cabeza y componiendo en una BufferedImage utilizando solo gráficos 2D y componentes livianos.

enter image description here

 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(); } } }); } } 

He reelaborado por completo tu ejemplo:

  • He simplificado lo que no era necesario ( setLocation() , constructor sin usar …)
  • He eliminado el código que desencadena un evento WINDOW_CLOSING (inútil)
  • He eliminado el código que restablece todas las ventanas para que vuelvan a estar visibles (lo que evitaría que GC las incluyera)
  • He usado un javax.swing.Timer lugar de un Thread para eliminar el diálogo
  • He usado un Thread para forzar GC (no es una buena idea en el EDT)
  • He modificado el criterio de éxito final para verificar que Window.getWindows() sea 2 (no 1 ), porque en Swing, si abre un cuadro de diálogo sin padre, se creará un marco invisible especial para usarlo como padre (para todos los diálogos sin propietario en realidad), una vez creado, ese marco no se puede eliminar.

El fragmento resultante es el siguiente:

 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(); } }); } } 

Las conclusiones importantes son:

  • No puede eliminar el marco invisible creado por Swing como padre de todos los cuadros de diálogo sin propietario
  • Debes forzar un GC para eliminar el diálogo eliminado de Window.getWindows() (ese parece un error para mí, pero creo que la razón es que Swing mantiene una WeakReference en todas las ventanas, y esta WeakReference no se lanza hasta un GC ha ocurrido.

Espero que esto dé una respuesta clara y completa a su problema.

con la intención de eliminar todas las dudas sobre EDT y confirmar sugerencia actualizada de trashgod, luego se envía a la consola

 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) 

del código

 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(); } }); } } 

No estoy seguro si su pregunta es sobre “recolección de basura” o sobre cómo identificar los diálogos que están visibles.

No puede controlar cuándo se realiza la recolección de basura. Invocar el método gc () es solo una sugerencia.

Si desea ignorar los diálogos “desechados”, puede usar el método isDisplayable () para verificar su estado.

Con el siguiente progtwig obtuve algunos resultados interesantes. El primer cambio que hice fue agregar algunos componentes al diálogo para que se utilizaran más recursos para cada diálogo, lo que boostía las posibilidades de que los recursos se recolectaran.

En mi máquina, encontré que si

a) crear 5 diálogos
b) cierra los diálogos
c) crear 5 diálogos

Entonces los primeros 5 parecen ser basura recolectada.

Sin embargo, si creo 5, entonces cierre y luego cree 1, luego cierre, parece que no funciona.

En resumen, no puede depender de cuándo se realizará la recolección de basura, por lo que le sugiero que use el método isDisplayable () para determinar cómo realizar su procesamiento. El botón “Mostrar cuadros de diálogo” utiliza este método como parte de la salida mostrada.

 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(); } }); } } 

Hay un tiempo de espera definido en AppContext antes de que finalmente se AppContext algunos recursos. Esto se establece en algo así como 5 segundos. Por lo tanto, si espera otros cinco segundos, el contexto eliminará la (última) referencia a su diálogo.

 wins = null; Thread.sleep(5000);