Eliminar contenedor de nivel superior en tiempo de ejecución

Resuelto mKorbel asked hace 13 años • 5 respuestas

Desafortunadamente, parece que esta pregunta recientemente cerrada no se entendió bien. 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)

Intentaré hacer esta pregunta nuevamente: ¿Cómo puedo matar en Runtime el primer nivel superior abierto Containery 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 < 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();
            }
        });
    }
}
mKorbel avatar Jun 10 '11 23:06 mKorbel
Aceptado

La invocación dispose()permite a la plataforma host recuperar la memoria consumida por el par pesado, pero no puede hacerlo hasta que el WINDOW_CLOSINGevento se procese en el archivo EventQueue. Incluso entonces, gc()es una sugerencia.

Anexo: Otra forma de ver la pesadilla es a través de un generador de perfiles. Al ejecutar el siguiente ejemplo con jvisualvm, se puede ver que la recopilación periódica nunca vuelve a la línea de base. He exagerado el eje vertical al comenzar con un montón artificialmente pequeño. Aquí se muestran ejemplos adicionales . Cuando la memoria es muy limitada, he utilizado dos enfoques:

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

  • Urgente: eliminar por completo el componente pesado, ejecutarlo sin cabeza y componer usando BufferedImagegráficos 2D y componentes livianos únicamente.

ingrese la descripción de la imagen aquí

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();
                }
            }
        });
    }
}
trashgod avatar Jun 10 '2011 17:06 trashgod

He reelaborado completamente tu ejemplo:

  • He simplificado lo que no era necesario ( setLocation(), constructor no utilizado...)
  • He eliminado el código que desencadena un evento WINDOW_CLOSING (inútil)
  • Eliminé el código que restablece todas las ventanas para que sean visibles nuevamente (lo que evitaría GC en ellas)
  • He usado a javax.swing.Timeren lugar de a Threadpara eliminar el diálogo.
  • He usado un Threadpara 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 abres un diálogo sin padre, se creará un marco invisible especial para usarlo como padre (en realidad, para todos los diálogos sin propietario) , 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 puedes eliminar el marco invisible creado por Swing como padre de todos los cuadros de diálogo sin propietario.
  • Tienes que forzar un GC para que se elimine el cuadro de diálogo eliminado Window.getWindows()(ese me parece un error, pero creo que la razón es que Swing mantiene un WeakReferenceen todas las ventanas, y esto WeakReferenceno se libera hasta que se haya producido un GC.

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

jfpoilpret avatar Jun 16 '2011 16:06 jfpoilpret

con la intención de disipar todas las dudas sobre EDT y confirmar la sugerencia actualizada de trashgod, luego la salida a la consola es

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();
            }
        });
    }
}
mKorbel avatar Jun 11 '2011 20:06 mKorbel

No estoy seguro de si su pregunta es sobre "recolección de basura" o sobre cómo identificar los cuadros de diálogo que son visibles.

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

Si desea ignorar los cuadros de diálogo "eliminados", puede utilizar el método isDisplayable() para comprobar su estado.

Con el siguiente programa obtuve algunos resultados interesantes. El primer cambio que hice fue agregar algunos componentes al cuadro de diálogo para que se usaran más recursos para cada cuadro de diálogo, lo que aumentaría la posibilidad de que los recursos se recolectaran como basura.

En mi máquina encontré que si

a) crear 5 cuadros de diálogo
b) cerrar los cuadros de diálogo
c) crear 5 cuadros de diálogo

Entonces los primeros 5 parecen ser basura recolectada.

Sin embargo, si creo 5, luego cierro, luego creo 1, luego cierro, no parece funcionar.

La conclusión es que no puede depender de cuándo se realizará la recolección de basura, por lo que le sugiero que utilice el método isDisplayable() para determinar cómo realizar el procesamiento. El botón "Mostrar cuadros de diálogo" utiliza este método como parte del resultado mostrado.

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();
            }
        });
    }
}
camickr avatar Jun 10 '2011 17:06 camickr