Occasional InterruptedException when quitting a Swing application
Asked Answered
D

17

27

I recently updated my computer to a more powerful one, with a quad-core hyperthreading processor (i7), thus plenty of real concurrency available. Now I'm occasionally getting the following error when quitting (System.exit(0)) an application (with a Swing GUI) that I'm developing:

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)

Well, given that it started to happen with a more concurrency-capable hardware, and it has to do with threads, and it happens occasionally, it's obviously some kind of timing thing. But the problem is that the stack trace is so short. All I have is the listing above. It doesn't include my own code at all, so it's somewhat hard to guess where the bug is.

Has anyone experienced something like this before? Any ideas how to start solving it?

Edit: Since quitting a Swing application with System.exit(0) may be "unclean", but I don't want to set the main frame to EXIT_ON_CLOSE because I want to ensure that there's nothing critical going on when the application quits, I added a mechanism so that it executes the main frame's dispose() method before calling System.exit(0). So it should be pretty clean now, but the occasional exception still happens. It happens after the System.exit(0) has been called; dispose() works with no problems. That is, it must be coming from a shutdown hook:

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

I even tried explicitly disposing all Windows by looping through Window.getWindows() array (it contains ownerless Dialogs and such), but it made no difference. This issue seems to have little to do with "cleanness" (i.e. explicitly releasing native screen resources before quitting). It's something else, but what?

Edit 2: Setting the default close operation to EXIT_ON_CLOSE made no difference. http://www.google.com/search?q=sun.java2d.Disposer.run(Disposer.java:125) finds a few bug reports, so maybe this indeed is a bug in Sun's Java2D implementation. I could imagine that bugs like this can go unfixed for a long time, because they're pretty harmless in practice; an exception from a shutdown hook hardly hurts anyone else. Given that this happens in a GUI app, the exception is not even noticed unless the stderr is directed to a console or log.

Deterrent answered 20/5, 2010 at 12:3 Comment(0)
N
16

Your Disposer is blocked in a call to remove() (removing the next platform native resource). This means that the disposer thread (a daemon thread) is not being shutdown naturally when the VM exits (which you should expect since you are terminating it via System.exit()).

You have a non-daemon thread in your application which is keeping the VM from exiting when all your swing windows have been disposed.

Solution: find it and cause it to exit.

Normally a swing application exits gracefully if all of its swing windows have been disposed of, for example, this program will pop a window then exit once it is closed (all with no call to System.exit()):

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

You might also try running the garbage collector before exiting, just for kicks.

Northeastwards answered 20/5, 2010 at 12:3 Comment(3)
Yes, I do have tens of threads around and it may well be that some of them are not shut down as elegantly as they should be. But I would be amazed if some of them were blocking Java2D resources; I'm well aware that those resources must be accessed only via their special thread and pretty sure that I'm doing so. But... is the only way to find out the annoying thread to go through all of them? I don't even have control over all of them, because many are created by 3rd party libraries. It's a pain that the stack trace is so short.Deterrent
It is not that you are blocking Java2D. The disposer thread is 'blocked' on a reference queue (thats what it is supposed to do). Your non-daemon threads are preventing the VM from 'naturally' being shutdown (which happens when all threads in the VM are marked as daemon). Look in the debugger, and see which non-daemon threads are running, see if there is a way to stop them. If they are 3rd party (with no stop() method), then there is nothing you can do.Northeastwards
Thanks, looking at the threads in debugger does shed some light on this issue. It seems to have to do with a thread called "Swing-Shell", which is created by JFileChooser. Indeed, when I don't open a JFileChooser, the app never crashes on shutdown. Sun bugs 6744953, 6741890 and 6713352 do report some issues with that thread. I'll investigate that further and file a bug report if I'm able to come up with a reproducible demo case.Deterrent
P
5

If you are using swing application then first call System.gc() and then call dispose() method. I think it will work fine.. I also use this.

Would like to up-vote this but I need more rep. This solution worked for me although I cannot find the explanation why and my co-worker also says it makes no sense.

I have 1.7 and a swing app that I have created, it reads in a file, rearranges the contents and then outputs to a file. has a run and quit button. uses preferences API, writer, reader, and a few other things. Upon opening and closing the app (without System.gc()) immediately only two times successively will return this same Exception as stated above. but with System.gc() right before dispose() I cannot get the Exception to throw again.

Propagandism answered 20/5, 2010 at 12:3 Comment(0)
R
2

I have the same issue ( Java 6 ). I noticed an unknwon sun.awt.image.ImageFetcher thread in the debugger. I think it comes from me using JButtons with Icons. The ImageFetcher thread disappears after 2 seconds. If the ImageFetcher thread is not running my programm exits just fine.

Rodd answered 20/5, 2010 at 12:3 Comment(0)
M
2

I think i have found where the bug comes from !

When you have a basic java application with an Exit submenu into a File menu... There is a shortcut to activate the exit Action...This shortcut must makes a circular link or something like that...

There is the solutions :

First of all this is optional to get all clear: Implements this Interface "WindowListener" from your main window.

At the construction of this main window do this :

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

implements the functions from the interface :

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) {}

After that you'll have to be careful about yours shortcuts ! You 'll have to remove the Action from the Exit Menu in order to manage it with an 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
}

I don't explain why but it works for me... I think that there is a kind of circular references... Hope to help you. See ya.

Mccartan answered 20/5, 2010 at 12:3 Comment(0)
C
2

I wonder if it is not related to this bug :

Java Bug 6489540

Basically it means that if Java2D is used when an InheritableThreadLocal object exists or a custom contextClassLoader is present they are captured and live forever causing memory leaks and possibly these kind of weird locks.

If this is the case a workaround would be to trigger a Java2D action on the main thread (i.e. immediately when the application starts) so the DisposerThread lives in a "squeky clean" environment. It is started by a static initializer so just a dummy load of a java2d resource and freeing it would be enough to get a DisposerThread which does not hang on to undesirable stuff.

Coloration answered 20/5, 2010 at 12:3 Comment(1)
Interesting bug, but there are no 'weird' locks in this case, the OP is not seeing any memory leak problems. His problem is a simple shutdown race condition between swing and the VM.Northeastwards
P
2

System.exit() probably isn't the cleanest way of closing down a Swing based app

Can't you set your main frame to EXIT_ON_CLOSE:

mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )

Then set it to invisible?

Related Q here; System.exit(0) in java

Poult answered 20/5, 2010 at 12:11 Comment(4)
Actually I've set the default close operation to DO_NOTHING, in order to prevent closing the app when something critical is going on. I'm listening to windowClosing events, and only quit the app when it's ok to do so. Now that I look at JFrame's source code, EXIT_ON_CLOSE actually does nothing fancy; it's simply System.exit(0).Deterrent
Sorry, it wasn't quite right. Actually EXIT_ON_CLOSE calls Window#processWindowEvent(WindowEvent) before System.exit(0). It in turn calls something called "AWTEventMulticaster", which indeed sounds like it may help in clean shutdown. Hmm, perhaps I could emulate EXIT_ON_CLOSE behavior without actually using it...?Deterrent
Found this which might offer a neat alternative: blog.srikanths.net/2009/01/on-never-using-systemexit.htmlPoult
I just tried this approach and still got the aforementioned exception in 1.6.0_24. Clever idea, but it didn't seem to work for me.Superman
T
1

I had the same problem, and found that I just had a Frame hiding in the background (I had it's visibility set to false). If I made sure to call .dispose() on my hidden frame and then .dispose() on my mainFrame I had no need to call System.exit(0), the application just cleaned itself up and shutdown. My code is Scala, but the idea is the same.

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(); }
}
Telesthesia answered 20/5, 2010 at 12:3 Comment(0)
M
1

There is the errors again sometimes but this seems to work fine :

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);
}
Mccartan answered 20/5, 2010 at 12:3 Comment(0)
N
1

It sounds like you have a thread running which hasn't terminated when you quit. Notably, the thread is wait()ing, and that method throws an interrupted exception if you try to stop the thread while it's running. I always set background threads to run as Daemons, which might help as well.

I would do the following in your 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();
  }
});

By manually breaking out of the loop, you allow the wait method to terminate (hopefully you're not waiting terribly long, are you? If you are, you might want to re-examine how you're utilizing your threads) and then get to a point in your code where it's safe to break - safe because you coded it to do so - and then the thread will terminate, letting the application terminate just fine.

Update Perhaps you are using the event dispatch thread inappropriately somewhere, and it's waiting/still working for you when you try to exit? The dispatcher thread should be doing as little work as possible, and should be passing anything complex off to another thread as quickly as it can. I'll admit I'm stabbing around in the dark a little bit, but I'm inclined to think it's not a bug, especially considering that you started noticing it on a more powerful machine - which to me screams "Race Condition!" not Java bug.

Ney answered 20/5, 2010 at 12:3 Comment(1)
I was already doing the kind of dispose. But probably I should check through all threads in the app, and shut down some of them in a more controlled fashion. Still it's weird to see the sun.java2d.disposer in the stack trace. I'm pretty sure I'm not accessing any graphics resources outside the event dispatch thread.Deterrent
C
0

Try this. This worked for me. I was creating JFileChooser instance which was not disposed properly after System.exit(0) call.

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent ev) {                	
                dispose();
                System.exit(0);
                }
 });
Crabwise answered 20/5, 2010 at 12:3 Comment(0)
C
0

I just ran into this error using databases. The problem was that the connection to the database was not closed. I solved this by using the following code :

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

Just throwing this out there for other people...

Cenogenesis answered 20/5, 2010 at 12:3 Comment(0)
G
0

This seems to be a bug resolved in Java 1.7.

Configured my Eclipse to run in jdk 1.7 and the error went away.

Yoav

Greensickness answered 20/5, 2010 at 12:3 Comment(0)
S
0

Try use EventQueue.invokeLater() to close your Swing.

Subterrane answered 20/5, 2010 at 12:3 Comment(0)
W
0

I was getting a similar error and somehow this worked for me:

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();
        }
    });
}
Windproof answered 20/5, 2010 at 12:3 Comment(0)
M
0

Threads in java only terminate when all of run() methods finish execution. Otherwise you will always have those Thread extended objects in VM messing around. You have to finish all the threads before exiting the Application.

Also consider that when you create your jFrame you are starting a Thread (CURRENT_THREAD I believe)

Mi answered 20/5, 2010 at 12:3 Comment(3)
Umm, creating a JFrame doesn't by itself start a thread. But probably I should shut down some of my threads in a more controlled fashion.Deterrent
java.sun.com/products/jfc/tsc/articles/threads/threads1.html .Here, I found a reference about it. It says "Swing components can be accessed by only one thread at a time. Generally, this thread is the event-dispatching thread".Mi
Yes, Swing components must be accessed via the EDT only, and the EDT gets created when any GUI resource is first accessed (at class loading time, I believe).Deterrent
F
0

If you were using the Swing App Framework, you could override Application.exit() to perform cleanup, or add an ExitListener as well. Otherwise, you could also add a shutdown hook, Runtime.getRuntime().addShutdownHook() to your app.

Fatuitous answered 20/5, 2010 at 12:3 Comment(0)
U
-1

If you are using swing application then first call System.gc() and then call dispose() method. I think it will work fine.. I also use this.

Universe answered 20/5, 2010 at 12:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.