Удалить контейнер верхнего уровня во время выполнения - PullRequest
18 голосов
/ 10 июня 2011

К сожалению, похоже, что этот недавно закрытый вопрос не был хорошо понят. Вот типичный вывод:

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)

Я попытаюсь задать этот вопрос еще раз: Как я могу k i l * l во время выполнения первого верхнего уровня Container и помочь мне с закрытием одного из 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();
            }
        });
    }
}

Ответы [ 5 ]

20 голосов
/ 10 июня 2011

Invoking dispose() позволяет платформе хоста восстанавливать память, занятую тяжеловесным одноранговым узлом, но это не может быть сделано до тех пор, пока после события WINDOW_CLOSING не будет обработано вEventQueue.Даже тогда gc() - это предложение.

Приложение: Еще один способ увидеть кошмар - через профилировщик.Запустив приведенный ниже пример с jvisualvm, можно увидеть, что периодическая коллекция никогда не вполне возвращается к базовой линии.Я преувеличил вертикальную ось, начав с искусственно небольшой кучи.Дополнительные примеры показаны здесь .Когда память очень ограничена, я использовал два подхода:

  • Emergent: цикл из командной строки, каждый раз запуская новую виртуальную машину.

  • Срочно: полностью исключить тяжелый компонент, запустив его без головы и создавая в BufferedImage, используя только 2D-графику и легкие компоненты.

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();
                }
            }
        });
    }
}
8 голосов
/ 16 июня 2011

Я полностью переработал ваш пример:

  • Я упростил то, что не было нужно (setLocation(), неиспользованный конструктор ...)
  • Я удалил код, который запускает событие WINDOW_CLOSING (бесполезно)
  • Я удалил код, который сбрасывает все окна, чтобы они снова стали видимыми (что помешает GC на них)
  • Я использовал javax.swing.Timer вместо Thread для удаления диалога
  • Я использовал Thread для форсирования GC (не очень хорошая идея в EDT)
  • Я изменил окончательный критерий успеха, чтобы проверить, что Window.getWindows() равно 2 (не 1 ), потому что в Swing, если вы открываете диалог без родителя, тогда будет создан специальный невидимый фрейм, чтобы использовать его в качестве родительского (на самом деле для всех диалогов, не имеющих владельца), после создания этот фрейм не может быть удален.

Полученный фрагмент выглядит следующим образом:

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 как родительский для всех диалогов без владельца
  • Вы должны заставить GC удалить удаленный диалог из Window.getWindows() (для меня это похоже на ошибку, но я думаю, причина в том, что Swing сохраняет WeakReference для всех окон, а это WeakReference не освобождается до тех пор, пока не произойдет GC.

Надеюсь, что это дает четкий и полный ответ на вашу проблему.

7 голосов
/ 12 июня 2011

с намерением развеять все сомнения по поводу EDT и подтвердить trashgod Обновленное предложение, затем вывод на консоль:

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();
            }
        });
    }
}
6 голосов
/ 10 июня 2011

Я не уверен, что вы спрашиваете о «сборке мусора» или о том, как идентифицировать видимые диалоговые окна.

Вы не можете контролировать, когда сборка мусора завершена. Вызов метода gc () - только предложение.

Если вы хотите игнорировать «удаленные» диалоги, вы можете использовать метод isDisplayable () для проверки его статуса.

С помощью следующей программы я получил интересные результаты. Первым изменением, которое я сделал, было добавление некоторых компонентов в диалог, чтобы больше ресурсов использовалось для каждого диалога, что увеличило бы вероятность того, что ресурсы будут собираться мусором.

На моей машине я обнаружил, что если я

а) создать 5 диалогов
б) закрыть диалоги
в) создать 5 диалогов

Тогда первые 5 выглядят как мусор.

Однако, если я создаю 5, затем закрываю, затем создаю 1, затем закрываю, похоже, это не сработает.

Суть в том, что вы не можете зависеть от того, когда будет выполняться сборка мусора, поэтому я предлагаю вам использовать метод isDisplayable (), чтобы определить, как выполнять вашу обработку. Кнопка «Показать диалоги» использует этот метод как часть отображаемого вывода.

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();
            }
        });
    }
}
3 голосов
/ 11 июня 2011

В AppContext определен тайм-аут перед окончательным освобождением некоторых ресурсов. Это установлено примерно на 5 секунд. Таким образом, если вы подождете еще пять секунд, контекст также будет располагать (последней) ссылкой на ваш диалог.

wins = null;
Thread.sleep(5000);
...