9

Thread.UncaughtExceptionHandler states that when the method which handles uncaught exceptions itself throws an exception, that exception will be ignored:

void uncaughtException(Thread t, Throwable e):

Method invoked when the given thread terminates due to the given uncaught exception.

Any exception thrown by this method will be ignored by the Java Virtual Machine.

However when I tested it, the JVM did not ignore the exceptions handled by the uncaught exception handler`:

public static void main(final String args[]) {
    Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread arg0, Throwable arg1) {
            throw new java.lang.RuntimeException("e2");
        }
    });
    throw new RuntimeException("e1");
}

Eclipse Console output (JRE 1.7):

Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread "main"

Another oddity I found out is that the output I get isn't coming from System.err. It seems to be from another stream altogether. I verified this by redirecting System.err to System.out, but I'm still getting "red" output:

public static void main(final String[] args) {
    System.setErr(System.out);
    System.out.println(System.err == System.out);
    System.err.println("this is black color");
    try {
        throw new Error("test stacktrace color");
    } catch (Throwable e) {
        e.printStackTrace();
    }
    try {
        Thread.sleep(2500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            throw new RuntimeException("from handler");
        }
    });
    throw new RuntimeException("from main");
}

The output (bolded signifies red color):

true

this is black color

java.lang.Error: test stacktrace color at asf.df.main(df.java:13)

Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread "main"

What's the explanation for these phenomenons?

What happens to errors thrown within UncaughtExceptionHandler? What's the expected (documented or guaranteed) behavior?

Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • Possibly version specific? – Andrew_CS Jul 18 '14 at 22:36
  • Even in the previous Java versions, the docs state that it should be ignored by the JVM. – Pacerier Jul 18 '14 at 22:39
  • In Oracle "1.6.0_39" / Ubuntu 12.04, the exception thrown from within the uncaught exception handler is swallowed by the JVM. Like @Andrew_CS said, it could be version/vendor specific. – srkavin Jul 18 '14 at 23:10
  • 2
    Define "did not ignore". It probably prints a stack trace but doesn't exit the thread that calls these methods, in the same way that the Swing event thread catches, prints, and ignores RTEs rather than exiting. – user207421 Jul 19 '14 at 00:38
  • @EJP, "ignore" is the keyword used by the docs. They didn't define it, but from the output I'm getting, it's likely "not ignored". – Pacerier Jul 19 '14 at 03:18
  • Perhaps Eclipse is trying to be "helpful"? – Raedwald Jul 19 '14 at 06:39

2 Answers2

5

HotSpot JVM prints the exceptions thrown from the UncaughtExceptionHandler. See JavaThread::exit

    if (HAS_PENDING_EXCEPTION) {
      ResourceMark rm(this);
      jio_fprintf(defaultStream::error_stream(),
            "\nException: %s thrown from the UncaughtExceptionHandler"
            " in thread \"%s\"\n",
            pending_exception()->klass()->external_name(),
            get_thread_name());
      CLEAR_PENDING_EXCEPTION;
    }

JVM prints these exceptions itself directly on stderr regardless of the System.err state - whether it was redirected or not.

Well, this kind of warning does not affect the application - in this sense the exception is "ignored". But you are right, this behavior is not obvious. Javadoc is misleading and is better to be fixed.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • +1, But isn't it the other way round, that HotSpot should be fixed to adhere to the JLS? – Pacerier Jul 20 '14 at 12:06
  • 1
    I don't think it is a good idea to ignore exceptions silently. Such cases would be hard to debug. BTW, this is just an API documentation which is not a part of JLS or JVMS. Javadoc tends to become obsolete as the implementation changes - this happens rather often due to performance optimizations, new features etc. For instance, the default `hashCode` is said to be usually implemented by converting an internal object address, but this is not true for HotSpot JVM. – apangin Jul 20 '14 at 14:51
2

The exceptions are ignored and processing continues when thrown from a non-main thread.

If it is thrown in main the error code returned is non-zero.

The unhandled exceptions are logged via syserr.

public static void main(final String[] args) {

    final Thread myThread = new Thread(new Runnable() {

        @Override
        public void run() {
            Thread.currentThread()
                .setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

                    @Override
                    public void uncaughtException(final Thread t, final Throwable e) {

                        System.out.println("In child UncaughtExceptionHandler at " + java.time.Instant.now());

                        throw new RuntimeException("From child thread UncaughtExceptionHandler"
                                + java.time.Instant.now());

                    }
                });
            throw new RuntimeException("from runnable");
        }
    });

    Thread.currentThread()
    .setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

        @Override
        public void uncaughtException(final Thread t, final Throwable e) {

                System.out.println("In main UncaughtExceptionHandler " + java.time.Instant.now());

                throw new RuntimeException("From main thread UncaughtExceptionHandler" + java.time.Instant.now());

        }
    });

    myThread.start();

    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));

    System.out.println("After child thread: " + java.time.Instant.now());

    //Will result in a non-zero return code
    throw new RuntimeException("from main");
}

Output:

In child UncaughtExceptionHandler at 2014-07-19T04:10:46.184Z

Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread "Thread-0" After child thread: 2014-07-19T04:10:48.197Z In main UncaughtExceptionHandler 2014-07-19T04:10:48.197Z

Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread "main"

Pacerier
  • 86,231
  • 106
  • 366
  • 634
Jeff
  • 3,712
  • 2
  • 22
  • 24
  • From your output, we see "Exception: java.lang.RuntimeException thrown from the UncaughtExceptionHandler in thread Thread-0". If so, why do you say that the exception is ignored when thrown from a non-main thread? – Pacerier Jul 19 '14 at 04:43
  • It writes to syserr but exceptions thrown in non-main thread handlers are not propagated and do not modify the return code on exit. From an execution stand point they are ignored. – Jeff Jul 19 '14 at 04:50
  • **1)** It does not write to system.error. I tried this by redirecting System.err to System.out (see the second code snippet in the question), but the output is still red color. **2)** And also, obviously exceptions thrown in different threads do not propagate to each other, regardless of whether we used UncaughtExceptionHandler or not.... – Pacerier Jul 19 '14 at 05:07
  • If it's red in Eclipse's console, unless the defaults have changed, it's standard error. Also verified running outside Eclipse and piping to standard error. I think once an exception is thrown from main System stream redirects (out,err) are lost. – Jeff Jul 19 '14 at 05:15
  • No, (look at the code in the question) I've already redirected System.err to System.out, so System.err.println will print black not red. If it's red, it indicates that it's *another* stream, not System.err any longer. – Pacerier Jul 19 '14 at 05:50
  • It appears those messages are being emitted directly from the JVM to standard error, it does not appear that manipulating System streams can change this behavior. – Jeff Jul 19 '14 at 06:46
  • Yes, that's what I was saying... (The question is thus:) Are these weird behaviors documented? What's the specification then, and Can we rely on them? – Pacerier Jul 19 '14 at 16:00
  • @apangin beat me to the source. – Jeff Jul 20 '14 at 02:35