27

我最近将我的计算机更新为功能更强大的计算机,配备四核超线程处理器 (i7),因此可以提供大量真正的并发性。现在,在退出( )我正在开发的应用程序(带有 Swing GUI)时,我偶尔会遇到以下错误:System.exit(0)

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通过循环遍历Window.getWindows()数组(它包含无主Dialogs 等)来显式处理所有 s,但它没有任何区别。这个问题似乎与“清洁度”(即在退出前显式释放原生屏幕资源)关系不大。这是另一回事,但什么?

编辑2:将默认关闭操作设置为EXIT_ON_CLOSE没有区别。http://www.google.com/search?q=sun.java2d.Disposer.run(Disposer.java:125)发现了一些错误报告,所以也许这确实是 Sun 的 Java2D 实现中的错误。我可以想象这样的错误可能会在很长一段时间内无法修复,因为它们在实践中是无害的;关闭挂钩的异常几乎不会伤害其他任何人。鉴于这发生在 GUI 应用程序中,除非将stderr其定向到控制台或日志,否则甚至不会注意到异常。

4

17 回答 17

16

您的 Disposer 在调用 remove() 时被阻止(删除下一个平台原生资源)。这意味着当 VM 退出时,disposer 线程(守护线程)不会自然关闭(这是您应该预料到的,因为您是通过 System.exit() 终止它)。

您的应用程序中有一个非守护程序线程,当您的所有摆动窗口都被释放时,它会阻止 VM 退出。

解决方案:找到它并使其退出。

通常,如果所有的 Swing 窗口都已被处理掉,则 Swing 应用程序会优雅地退出,例如,该程序将弹出一个窗口,然后在它关闭后退出(所有这些都没有调用 System.exit()):

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

您也可以尝试在退出之前运行垃圾收集器,只是为了好玩。

于 2010-06-01T13:14:48.337 回答
5

如果您使用的是 swing 应用程序,则首先调用 System.gc() 然后调用 dispose() 方法。我认为它会很好用。我也用这个。

想对此投票,但我需要更多代表。这个解决方案对我有用,虽然我找不到原因,我的同事也说这没有意义。

我有 1.7 和我创建的一个 Swing 应用程序,它读入一个文件,重新排列内容,然后输出到一个文件。有一个运行和退出按钮。使用首选项 API、编写器、读取器和其他一些东西。在立即打开和关闭应用程序(不带System.gc())时,仅连续两次将返回与上述相同的异常。但System.gc()就在dispose()我无法再次抛出异常之前。

于 2013-05-02T14:41:15.673 回答
2

System.exit() 可能不是关闭基于 Swing 的应用程序的最干净的方法

您不能将主框架设置为 EXIT_ON_CLOSE:

mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )

然后设置为不可见?

相关Q在这里;java中的System.exit(0)

于 2010-05-20T12:11:53.557 回答
2

我想知道它是否与此错误无关:

Java 错误 6489540

基本上,这意味着如果在存在 InheritableThreadLocal 对象或存在自定义 contextClassLoader 时使用 Java2D,它们将被捕获并永远存在,从而导致内存泄漏和可能出现这些奇怪的锁。

如果是这种情况,解决方法是在主线程上触发 Java2D 操作(即在应用程序启动时立即),以便 DisposerThread 处于“干净整洁”的环境中。它由一个静态初始化程序启动,因此只需一个 java2d 资源的虚拟负载并释放它就足以获得一个不会挂在不需要的东西上的 DisposerThread。

于 2010-05-27T16:40:59.187 回答
2

我想我已经找到了错误的来源!

当您有一个带有退出子菜单的基本 java 应用程序进入文件菜单时...有一个快捷方式可以激活退出操作...此快捷方式必须建立一个循环链接或类似的东西...

有解决方案:

首先,为了清楚起见,这是可选的:从您的主窗口实现此接口“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
}

我不解释为什么,但它对我有用......我认为有一种循环引用......希望对你有所帮助。拜拜。

于 2011-07-07T14:18:12.330 回答
2

我有同样的问题(Java 6)。我注意到调试器中有一个未知的 sun.awt.image.ImageFetcher 线程。我认为它来自我使用带有图标的 JButtons。ImageFetcher 线程在 2 秒后消失。如果 ImageFetcher 线程没有运行我的程序退出就好了。

于 2011-11-14T01:18:51.240 回答
1

听起来您有一个线程正在运行,但在您退出时并未终止。值得注意的是,线程正在等待(),如果您尝试在线程运行时停止线程,该方法将引发中断异常。我总是将后台线程设置为作为守护进程运行,这也可能有所帮助。

我会在您的 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错误。

于 2010-05-28T17:42:28.660 回答
1

有时会再次出现错误,但这似乎工作正常:

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);
}
于 2011-07-07T14:25:23.533 回答
1

我遇到了同样的问题,发现我只是在背景中隐藏了一个 Frame(我将它的可见性设置为 false)。如果我确保在我的隐藏框架上调用 .dispose(),然后在我的 mainFrame 上调用 .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(); }
}
于 2011-12-29T21:36:01.193 回答
0

如果您使用的是Swing App Framework,则可以覆盖Application.exit()以执行清理,或者也添加一个ExitListener。否则,您还可以Runtime.getRuntime().addShutdownHook()向您的应用程序添加关闭挂钩。

于 2010-05-28T13:59:18.497 回答
0

java 中的线程仅在所有 run() 方法执行完毕时才会终止。否则,您将始终让 VM 中的那些 Thread 扩展对象乱七八糟。您必须在退出应用程序之前完成所有线程。

还要考虑当您创建 jFrame 时,您正在启动一个线程(我相信是 CURRENT_THREAD)

于 2010-05-28T18:36:58.493 回答
0

我遇到了类似的错误,不知何故这对我有用:

private static void createAndShowGUI() {
  JFrame jf = new MyProgram();
  jf.setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        public void run() {
          createAndShowGUI();
        }
    });
}
于 2011-08-16T09:52:35.083 回答
0

尝试使用EventQueue.invokeLater()关闭您的 Swing。

于 2012-11-06T18:53:53.797 回答
0

这似乎是 Java 1.7 中解决的错误。

将我的 Eclipse 配置为在 jdk 1.7 中运行并且错误消失了。

约夫

于 2013-03-29T21:58:21.277 回答
0

我刚刚使用数据库遇到了这个错误。问题是与数据库的连接没有关闭。我通过使用以下代码解决了这个问题:

Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    });

只是把它扔在那里给其他人......

于 2014-04-11T19:19:12.807 回答
0

试试这个。这对我有用。我正在创建 JFileChooser 实例,该实例在 System.exit(0) 调用后未正确处理。

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent ev) {                	
                dispose();
                System.exit(0);
                }
 });

于 2014-11-11T10:13:28.277 回答
-1

如果您使用的是 swing 应用程序,则首先调用 System.gc() 然后调用 dispose() 方法。我认为它会很好用。我也用这个。

于 2012-05-03T13:08:49.817 回答