71

This is probably a very naive question.

I used to believe that a Throwable in Java always contains the stack trace. Is it correct?

Now it looks like that I catch exceptions without the stack trace. Does it make sense? Is it possible to catch an exception without the stack trace?

Lii
  • 11,553
  • 8
  • 64
  • 88
Michael
  • 41,026
  • 70
  • 193
  • 341
  • 4
    What JVM? Environment? etc. Does this help? http://stackoverflow.com/questions/4659151/recurring-exception-without-a-stack-trace-how-to-reset – NG. Jul 11 '12 at 14:07
  • 1
    @SB. Yes, this does help. Thanks a lot. I have a very similar problem: I have a lot of exceptions (NPE). Method `error` of `log4j` logs some exceptions _without_ the stack trace. – Michael Jul 11 '12 at 14:25

5 Answers5

40

It's possible to catch a Throwable object in Java without a stack trace:

Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) 

Constructs a new throwable with the specified detail message, cause, suppression enabled or disabled, and writable stack trace enabled or disabled.

public Throwable fillInStackTrace()

Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread.

If the stack trace of this Throwable is not writable, calling this method has no effect.

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html

Alex W
  • 37,233
  • 13
  • 109
  • 109
  • 2
    It just says "writeable stack trace" enabled or disabled.. I don't think it means it won't catch the stacktrace – MatanCo Jun 19 '19 at 09:18
  • Does this mean it won't be generated and the process will be a lot faster? – Paul Oct 16 '19 at 09:33
  • 1
    The documentation for `writableStackTrace` says this: "whether or not the stack trace should be _writable_". Hence that parameter does NOT prevent stack traces from being generated. I don't see how this post answers the question. Please fix or delete this answer! – Lii Apr 01 '20 at 11:52
  • 1
    Please read the question. OP is asking if it's possible to catch a `Throwable` without a stack trace. The answer is of course yes. Direct quote from documentation for `fillInStackTrace`: "Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread. If the stack trace of this Throwable is not writable, calling this method has no effect." – Alex W May 15 '20 at 17:47
37

For Java 6:

As Java 6 doesn't have the Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) constructor, we can suppress the stacktrace filling using below technique (borrowed from Scala, came to know from How slow are Java exceptions?)

class NoStackTraceRuntimeException extends RuntimeException {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

Usage is same: throw new NoStackTraceRuntimeException (), or it's subtypes.

We can also do the same by extending Throwable:

class NoStackTraceThrowable extends Throwable {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

But, a small catch is that you no longer can catch these exception using Exception as this is not subtype of Exception, instead should catch NoStackTraceThrowable or it's subtypes.

Update: For some interesting stats on performance in different usecases, check this SO question

Community
  • 1
  • 1
manikanta
  • 8,100
  • 5
  • 59
  • 66
  • 5
    @shekharsuman No, but neither Java 7. This answer might help someone with Java 6 :) – manikanta Aug 15 '14 at 10:09
  • 3
    Even outside of Java 1.6, this is still a viable method. This will suppress the entire stack trace (as the passed in 'cause' of the 4 parameter constructor will be printed out). – DBK May 08 '15 at 13:17
  • I'm still using Java 6 here in 2016, so yeah :/ – Charles Wood Mar 23 '16 at 22:01
  • 4
    Your `NoStackTraceThrowable` could extend `Exception` instead (and be called `NoStackTraceException`) if you wanted it to be caught by a `catch (Exception)` block. – dimo414 Apr 03 '16 at 17:11
  • 5
    There is no need to mark a method merely returning `this` as `synchronized`. – Holger Nov 01 '19 at 16:33
17

For Java 7+, here is an example of an exception where the stack trace can optionally be suppressed.

public class SuppressableStacktraceException extends Exception {

    private boolean suppressStacktrace = false;

    public SuppressableStacktraceException(String message, boolean suppressStacktrace) {
        super(message, null, suppressStacktrace, !suppressStacktrace);
        this.suppressStacktrace = suppressStacktrace;
    }

    @Override
    public String toString() {
        if (suppressStacktrace) {
            return getLocalizedMessage();
        } else {
            return super.toString();
        }
    }
}

This can be demonstrated with:

try {
    throw new SuppressableStacktraceException("Not suppressed", false);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}
try {
    throw new SuppressableStacktraceException("Suppressed", true);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}

This is based on the MLContextException from Apache SystemML, the code of which is available on GitHub at https://github.com/apache/systemml.

Deron
  • 196
  • 1
  • 5
10

The easiest way to suppress the stacktrace on any Exception is

throwable.setStackTrace(new StackTraceElement[0]);

If the exception has a cause, you may need to do the same recursively.

This also reduces the costly creation of the stack trace, as much as possible

The stacktrace for a throwable is initialized in

Throwable#fillInStackTrace()

, which is called by any constructor and thus cannot be avoided. When the stacktrace actually is used, an StackTraceElement[] is lazily constructed in

Throwable#getOurStackTrace()

which only happens, if the field Throwable.stackTrace was not already set.

Setting the stacktrace to whatever non null value, avoids the construction of the StackTraceElement[] in Throwable#getOurStackTrace() and reduces the performance penalty as much as possible.

  • 5
    This works, but it doesn't solve the problem that most people want to solve by suppressing stack traces. Generating a stack trace is slow, and this can slow down code that uses exceptions a lot for messaging. To address this, one must prevent the creation of the stack traces, not delete them when they have already been created. - That said, your approach could still be useful to save memory when a lot of exceptions over which one has no control are stored for some reason. –  Nov 04 '16 at 19:57
  • Halo @Hans Adler, I understand your concern, but I think my answer also reduces the costly creation of the stack trace, as much as possible.Please see my last edit. – Stefan Isele - prefabware.com Nov 06 '16 at 10:27
  • *“[Throwable#fillInStackTrace()] is called by any constructor and thus cannot be avoided.”* – You can override this method in order to make it a no-op. – JojOatXGME May 26 '21 at 09:44
2

As hinted at in the reply by NG., if you are seeing the stacktrace at first but then it disappears for the same exception, you are most likely seeing the effects of JVM optimization. In that case, the following questions are very similar.

Recurring Exception without a stack trace - how to reset?

NullPointerException in Java with no StackTrace

You can turn this JVM feature off, but I would recommend keeping it enabled and working towards preventing the error in the first place, or catching it and handling it more gracefully.

rougou
  • 956
  • 9
  • 14