Случайное InterruptedException при выходе из приложения Swing - PullRequest
27 голосов
/ 20 мая 2010

Я недавно обновил свой компьютер до более мощного, с четырехъядерным процессором Hyper-Threading (i7), таким образом, было много реального параллелизма. Теперь я иногда получаю следующую ошибку при выходе (System.exit(0)) из приложения (с графическим интерфейсом Swing), которое я разрабатываю:

Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
        at java.lang.Object.wait(Native Method)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
        at sun.java2d.Disposer.run(Disposer.java:125)
        at java.lang.Thread.run(Thread.java:619)

Что ж, учитывая, что это начало происходить с аппаратным обеспечением с более высокой степенью параллелизма, оно имеет отношение к потокам, и иногда это происходит, очевидно, это какая-то временная вещь. Но проблема в том, что трассировка стека такая короткая. Все, что у меня есть, это список выше. Он вообще не включает в себя мой собственный код, поэтому довольно сложно догадаться, где ошибка.

Кто-нибудь испытывал что-то подобное раньше? Есть идеи, как начать ее решать?

Редактировать: Поскольку выход из приложения Swing с System.exit(0) может быть «нечистым», но я не хочу устанавливать основной фрейм на EXIT_ON_CLOSE, потому что я хочу убедиться, что ничего критического продолжая работу при выходе из приложения, я добавил механизм, чтобы он выполнял метод dispose() основного фрейма перед вызовом System.exit(0). Так что теперь это должно быть довольно чисто, но случайное исключение все же случается. Это происходит после того, как System.exit(0) был вызван; dispose() работает без проблем. То есть, это должно происходить от отключающего крюка:

mainFrame.dispose(); // No problem! After this returns, all visible GUI is gone.
// In fact, if there were no other threads around, the VM could terminate here.
System.exit(0); // Throws an InterruptedException from sun.java2d.Disposer.run

Я даже пытался явно удалить все Window s, просматривая массив Window.getWindows() (он содержит Dialog s без владельца и тому подобное), но это не имело никакого значения. Эта проблема, похоже, не имеет ничего общего с «чистотой» (то есть явным освобождением ресурсов собственного экрана перед выходом). Это что-то еще, но что?

Редактировать 2: Установка операции закрытия по умолчанию на EXIT_ON_CLOSE не изменила. http://www.google.com/search?q=sun.java2d.Disposer.run(Disposer.java:125) находит несколько отчетов об ошибках, так что, возможно, это действительно ошибка в реализации Sun Java2D. Я мог бы предположить, что подобные ошибки могут оставаться незафиксированными в течение длительного времени, потому что они довольно безопасны на практике; исключение из крюка отключения вряд ли повредит кому-либо еще. Учитывая, что это происходит в приложении с графическим интерфейсом, исключение даже не замечается, если stderr не направлен на консоль или журнал.

Ответы [ 17 ]

15 голосов
/ 01 июня 2010

Ваш Disposer заблокирован в вызове remove () (удаление следующего ресурса собственной платформы). Это означает, что поток утилизации (поток демона) не завершает работу естественным образом при выходе из виртуальной машины (чего следует ожидать, поскольку вы завершаете его с помощью System.exit ()).

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

Решение : найдите его и заставьте выйти.

Обычно свинг-приложение завершается корректно, если все его свинг-окна были удалены, например, эта программа выскочит окно, а затем закроется, когда оно закроется (все без вызова System.exit ()):

public static void main(String args[]) throws Exception {
    JFrame jf = new JFrame();
    jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    jf.setVisible(true);
}

Вы также можете попробовать запустить сборщик мусора перед выходом, просто для удовольствия.

4 голосов
/ 02 мая 2013

Если вы используете приложение Swing, сначала вызовите System.gc (), а затем вызовите метод dispose (). Я думаю, что это будет работать нормально .. Я также использую это.

Хотел бы проголосовать, но мне нужно больше респ. Это решение сработало для меня, хотя я не могу найти объяснение, почему, и мой коллега также говорит, что это не имеет смысла.

У меня есть 1.7 и приложение Swing, которое я создал, оно читает в файл, переупорядочивает содержимое и затем выводит в файл. имеет кнопку запуска и выхода. использует настройки API, писателя, читателя и несколько других вещей. После открытия и закрытия приложения (без System.gc()) сразу только два раза подряд будет возвращаться это же исключение, как указано выше. но с System.gc() прямо перед dispose() я не могу заставить Исключение бросить снова.

2 голосов
/ 14 ноября 2011

У меня та же проблема (Java 6). Я заметил незнакомую тему sun.awt.image.ImageFetcher в отладчике. Я думаю, что это исходит от меня, используя JButtons с иконками. Поток ImageFetcher исчезает через 2 секунды. Если поток ImageFetcher не запущен, моя программа завершает работу нормально.

2 голосов
/ 27 мая 2010

Интересно, не связано ли это с этой ошибкой:

Java Bug 6489540

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

Если это так, то в качестве обходного пути можно было бы запустить действие Java2D в главном потоке (т. Е. Сразу после запуска приложения), чтобы DisposerThread находился в «вялой чистой» среде. Он запускается статическим инициализатором, поэтому просто фиктивной загрузки ресурса java2d и его освобождения будет достаточно, чтобы получить DisposerThread, который не привязан к нежелательным вещам.

2 голосов
/ 07 июля 2011

Я думаю, я нашел, откуда исходит ошибка!

Если у вас есть базовое Java-приложение с подменю Exit в меню File ... Существует ярлык для активации действия выхода ... Этот ярлык должен делать круглую ссылку или что-то в этом роде ...

Есть решения:

Прежде всего, это необязательно, чтобы все было ясно: Реализует этот интерфейс "WindowListener" из вашего главного окна.

При построении этого главного окна сделайте это:

JFrame frame=this.getFrame();
if(frame!=null)
{
   Window[] windows=frame.getWindows();
   for(Window window : windows)
   window.addWindowListener(this);
}

реализует функции из интерфейса:

public void windowOpened(WindowEvent e) {}

public void windowClosing(WindowEvent e) {
    JFrame frame=this.getFrame();
    if(frame!=null)
    {
        Window[] windows=frame.getOwnedWindows();
        for(Window window : windows)
        {
            window.removeWindowListener(this);
            window.dispose();
        }
    }
    //clear();
    System.gc();
}

public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}

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

private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
    //this.clear();
    svExitMenuItem.removeActionListener(null);//this call removes the end error from this way to exit.
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);//null to avoid end error too
}

Я не объясняю почему, но это работает для меня ... Я думаю, что есть своего рода циклические ссылки ... Надеюсь помочь вам. Увидимся.

2 голосов
/ 20 мая 2010

System.exit (), вероятно, не самый чистый способ закрытия приложения на основе Swing

Не можете ли вы установить для основного кадра значение EXIT_ON_CLOSE:

mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )

Затем установитеэто невидимым?

Связанный Q здесь; System.exit (0) в Java

1 голос
/ 30 декабря 2011

У меня была та же проблема, и я обнаружил, что у меня только что рамка скрывалась на заднем плане (у меня была видимость установлена ​​на false). Если я обязательно вызвал .dispose () на своем скрытом фрейме, а затем .dispose () на своем мейнфрейме, мне не нужно было вызывать System.exit (0), приложение просто самоочищалось и закрывалось. Мой код - Scala, но идея та же.

def top = new MainFrame {

   import javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE
   peer.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE)
   override def closeOperation() { endExecution; PreviewFrame.dispose(); this.dispose(); }
}
1 голос
/ 07 июля 2011

Иногда снова возникают ошибки, но, кажется, это работает нормально:

private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {
    svExitMenuItem.removeActionListener(null);
    windowClosing(null);//i add it to clear and call the garbadge collector...
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);
}
1 голос
/ 28 мая 2010

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

Я бы сделал следующее в вашем JFrame:

myJFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent we) {
    // Some pieces of code to instruct any running threads, possibly including
    // this one, to break out of their loops

    // Use this instead of System.exit(0) - as long as other threads are daemons,
    // and you dispose of all windows, the JVM should terminate.
    dispose();
  }
});

Вручную вырвавшись из цикла, вы позволяете завершить метод ожидания (надеюсь, вы не ждете слишком долго, не так ли? а затем перейдите к точке в вашем коде, где безопасно взломать - безопасно, потому что вы его запрограммировали, - и тогда поток завершится, позволяя приложению завершиться очень хорошо.

Обновление Возможно, вы где-то ненадлежащим образом используете поток рассылки событий, и он ожидает / все еще работает для вас, когда вы пытаетесь выйти? Поток диспетчера должен выполнять как можно меньше работы и как можно быстрее передавать что-либо сложное другому потоку. Я признаю, что немного пробираюсь в темноте, но я склонен думать, что это не ошибка, особенно если учесть, что вы начали замечать это на более мощной машине - что для меня кричит "Состояние гонки!" не ошибка Java.

0 голосов
/ 30 марта 2013

Кажется, это ошибка, устраненная в Java 1.7.

Настроил Eclipse для работы в jdk 1.7, и ошибка исчезла.

Yoav

...