Remover o contêiner de nível superior no tempo de execução

Infelizmente, parece que esta questão recentemente encerrada não foi bem compreendida. Aqui está a saída típica:

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) 

Tentarei fazer essa pergunta novamente: Como posso criar o primeiro Container nível superior em tempo de execução e ajudar a fechar um dos 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(); } }); } } 

A chamada dispose() permite que a plataforma host recupere a memory consumida pelo peer pesado, mas não pode fazê-lo até que o evento WINDOW_CLOSING seja processado no EventQueue . Mesmo assim, gc() é uma sugestão.

Adenda: Outra maneira de ver o pesadelo é através de um profiler. Executando o exemplo abaixo com jvisualvm , pode-se ver que a coleção periódica nunca retorna à linha de base. Eu exagerei o eixo vertical começando com um monte artificialmente pequeno. Exemplos adicionais são mostrados aqui . Quando a memory é muito limitada, usei duas abordagens:

  • Emergente: faz um loop na linha de comando, iniciando uma nova VM a cada vez.

  • Urgente: Elimine todo o componente pesado, executando sem cabeça e compondo em um BufferedImage usando somente charts 2D e componentes leves.

insira a descrição da imagem aqui

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

Eu reformulei completamente o seu exemplo:

  • Eu simplifiquei o que não era necessário ( setLocation() , construtor não utilizado …)
  • Eu removi o código que aciona um evento WINDOW_CLOSING (inútil)
  • Eu removi o código que redefine todas as janelas para visível novamente (o que impediria o GC nelas)
  • Eu usei um javax.swing.Timer vez de um Thread para descartar o diálogo
  • Eu usei um Thread para forçar o GC (não é uma boa ideia no EDT)
  • Eu modifiquei o critério de sucesso final para verificar se Window.getWindows() é 2 (não 1 ), porque em Swing, se você abrir um diálogo sem pai, então um quadro invisível especial será criado para usá-lo como pai (por todos os diálogos sem dono, na verdade), uma vez criados, esse quadro não pode ser removido.

O snippet resultante segue:

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

As conclusões importantes são:

  • Você não pode remover o quadro invisível criado pelo Swing como pai de todos os diálogos sem dono
  • Você tem que forçar um GC para que o diálogo descartado seja removido de Window.getWindows() (aquele parece um bug para mim, mas eu acho que o motivo é que o Swing mantém um WeakReference em todas as janelas, e este WeakReference não é liberado até um GC ocorreu.

Espero que isso dê uma resposta clara e completa ao seu problema.

com a intenção de afastar todas as dúvidas sobre o EDT e confirmar a sugestão atualizada do trashgod, então a saída para o console é

 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) 

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

Não tenho certeza se você pergunta sobre “garbage collection” ou sobre como identificar diálogos visíveis.

Você não pode controlar quando a garbage collection é feita. Invocar o método gc () é apenas uma sugestão.

Se você quiser ignorar checkboxs de diálogo “descartadas”, poderá usar o método isDisplayable () para verificar seu status.

Com o seguinte programa, obtive alguns resultados interessantes. A primeira mudança que fiz foi adicionar alguns componentes à checkbox de diálogo para que mais resources fossem usados ​​para cada checkbox de diálogo, o que aumentaria a chance de os resources serem coletados como lixo.

Na minha máquina, descobri que se eu

a) crie 5 diálogos
b) feche os diálogos
c) criar 5 diálogos

Então os primeiros 5 parecem ser lixo coletado.

No entanto, se eu criar 5, em seguida, feche, crie 1 e, em seguida, feche, não parece funcionar.

A linha inferior é que você não pode depender de quando a garbage collection será feita, então eu sugiro que você use o método isDisplayable () para determinar como fazer o seu processamento. O botão “Exibir Diálogos” usa esse método como parte da saída exibida.

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

Há um tempo limite definido no AppContext antes que alguns resources sejam liberados finalmente. Isso está definido para algo como 5 segundos. Assim, se você esperar por mais cinco segundos, o contexto também descartará a (última) referência ao seu diálogo.

 wins = null; Thread.sleep(5000);