ChatGPT解决这个技术问题 Extra ChatGPT

NullPointerException in Java with no StackTrace

I've had instances of our Java code catch a NullPointerException, but when I try to log the StackTrace (which basically ends up calling Throwable.printStackTrace() ), all I get is:

java.lang.NullPointerException

Has anyone else come across this? I tried googling for "java null pointer empty stack trace" but didn't come across anything like this.

What is the context? Are there multiple threads involved? I've had issues trying to get the stack trace of an exception in a SwingWorker.
No threading involved here, just plain old Java.
@Bozho - nope - not sure how to reproduce the NullPointer yet.
More info on -XX:-OmitStackTraceInFastThrow in the dup: stackoverflow.com/questions/4659151/…

R
Roland Illig

You are probably using the HotSpot JVM (originally by Sun Microsystems, later bought by Oracle, part of the OpenJDK), which performs a lot of optimization. To get the stack traces back, you need to pass the option -XX:-OmitStackTraceInFastThrow to the JVM.

The optimization is that when an exception (typically a NullPointerException) occurs for the first time, the full stack trace is printed and the JVM remembers the stack trace (or maybe just the location of the code). When that exception occurs often enough, the stack trace is not printed anymore, both to achieve better performance and not to flood the log with identical stack traces.

To see how this is implemented in the HotSpot JVM, grab a copy of it and search for the global variable OmitStackTraceInFastThrow. Last time I looked at the code (in 2019), it was in the file graphKit.cpp.


Thanks for the tip. Any idea if there are any hidden gotchas to passing this option (it seems pretty innocuous as long as my application doesn't throw a ton of exceptions)?
There are no hidden gotchas that I know of. When you look at the Hotspot source code, you can see that this option is only used in one place (graphKit.cpp). And that looks fine to me.
Thought I'd add the additional bit of information that when the stack trace gets optimized away, it's because it has gotten fully handled at least once: jawspeak.com/2010/05/26/…
I am running an OpenJDK JVM, version 1.8.0u171 (Debian 9), and it seems to accept the -XX:-OmitStackTraceInFastThrow flag as well. I've yet to confirm if that was why I was also failing to print stack-traces (e.g., using e.printStackTrace), but it seems highly likely. I've expanded the answer to reflect this discovery.
In our case first 125 exceptions had a stack trace, and then the rest across 3 rotations of log files had none. This answer was very helpful in finding the culprit.
S
Steven Schlansker

As you mentioned in a comment, you're using log4j. I discovered (inadvertently) a place where I had written

LOG.error(exc);

instead of the typical

LOG.error("Some informative message", e);

through laziness or perhaps just not thinking about it. The unfortunate part of this is that it doesn't behave as you expect. The logger API actually takes Object as the first argument, not a string - and then it calls toString() on the argument. So instead of getting the nice pretty stack trace, it just prints out the toString - which in the case of NPE is pretty useless.

Perhaps this is what you're experiencing?


+1: This would explain the described behavior, and you are not the only one who discovered this :)
We actually have a standard policy of never using the first form above (LOG.error(exc);) - we always use the 2 parameter signature so that we add some descriptive statement to the logs instead of just a raw stacktrace.
Sure, but policy doesn't mean it's always executed correctly! Figured it was worth mentioning, at least.
True, but in this case it was ;-)
M
Matt Solnit

We have seen this same behavior in the past. It turned out that, for some crazy reason, if a NullPointerException occurred at the same place in the code multiple times, after a while using Log.error(String, Throwable) would stop including full stack traces.

Try looking further back in your log. You may find the culprit.

EDIT: this bug sounds relevant, but it was fixed so long ago it's probably not the cause.


The bug is closed, but the -XX:-OmitStackTraceInFastThrow flag is still needed to workaround the performance optimization.
I've been seeing this recently a lot. Any clues as to what might be causing this, or how to fix it? The logging system may have been up for days, and the actual cause rotated out, nevermind the tedious search...
Pawel, have you tried the -XX:-OmitStackTraceInFastThrow JVM flag suggested by Joshua? See also stackoverflow.com/a/2070568/6198.
This was it for us. Thanks.
B
Benoît Guérout

Here is an explanation : Hotspot caused exceptions to lose their stack traces in production – and the fix

I've tested it on Mac OS X

java version "1.6.0_26"

Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511)

Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode) Object string = "abcd"; int i = 0; while (i < 12289) { i++; try { Integer a = (Integer) string; } catch (Exception e) { e.printStackTrace(); } }

For this specific fragment of code, 12288 iterations (+frequency?) seems to be the limit where JVM has decided to use preallocated exception...


P
Peter Lang

exception.toString does not give you the StackTrace, it only returns

a short description of this throwable. The result is the concatenation of: * the name of the class of this object * ": " (a colon and a space) * the result of invoking this object's getLocalizedMessage() method

Use exception.printStackTrace instead to output the StackTrace.


Sorry, I misspoke in my original post. I'm logging these through Log4J, which does use printStackTrace().
Have you tried using getStackTrace() to make sure the problem is not with your logger?
If you are using log4j, be sure to send the exception as part of the argument to the log method. I will post an answer with that.
@raviaw valid point! @Edward Shtern: can you confirm that you definitely are using the 2-arg form of the log4j method? I know you mentioned in an answer further down that it is the company policy to do so, but are you ABSOLUTELY sure that in this case you are following the policy?
It might be a long shot, but is it possible that the exception originates in some 3rd party code? Maybe it is a (poorly written) exception wrapper, whose toString() simply returns the class name of the wrapped exception, and which fails to provide the underlying stack trace. Try put something like logger.info("Exception class = " + exc.class.getCanonicalName()) into your catch block and see what you get.
S
Steven Schlansker

Alternate suggestion - if you're using Eclipse, you could set a breakpoint on NullPointerException itself (in the Debug perspective, go to the "Breakpoints" tab and click on the little icon that has a ! in it)

Check both the "caught" and "uncaught" options - now when you trigger the NPE, you'll immediately breakpoint and you can then step through and see how exactly it is handled and why you're not getting a stack trace.


S
Sheldon Young

toString() only returns the exception name and the optional message. I would suggest calling

exception.printStackTrace()

to dump the message, or if you need the gory details:

 StackTraceElement[] trace = exception.getStackTrace()

See above - I misspoke - I am using printStackTrace().
M
Michael D. Irizarry

This will output the Exception, use only to debug you should handle you exceptions better.

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }

S
Stephen C

(Your question is still unclear on whether your code is calling printStackTrace() or this is being done by a logging handler.)

Here are some possible explanations about what might be happening:

The logger / handler being used has been configured to only output the exception's message string, not a full stack trace.

Your application (or some third-party library) is logging the exception using LOG.error(ex); rather than the 2-argument form of (for example) the log4j Logger method.

The message is coming from somewhere different to where you think it is; e.g. it is actually coming some third-party library method, or some random stuff left over from earlier attempts to debug.

The exception that is being logged has overloaded some methods to obscure the stacktrace. If that is the case, the exception won't be a genuine NullPointerException, but will be some custom subtype of NPE or even some unconnected exception.

I think that the last possible explanation is pretty unlikely, but people do at least contemplate doing this kind of thing to "prevent" reverse engineering. Of course it only really succeeds in making life difficult for honest developers.


R
Roland Illig

When you are using AspectJ in your project, it may happen that some aspect hides its portion of the stack trace. For example, today I had:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

This stack trace was printed when running the test via Maven's surefire.

On the other hand, when running the test in IntelliJ, a different stack trace was printed:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)