Java中没有StackTrace的NullPointerException

我有我们的Java代码的实例捕捉NullPointerException,但当我试图记录StackTrace(基本上最终调用Throwable.printStackTrace()),我得到的是:

java.lang.NullPointerException

有人遇到过吗?我试着在谷歌上搜索“java空指针空堆栈跟踪”,但没有遇到这样的东西。

141810 次浏览

exception.toString不会给你StackTrace,它只返回

这个throwable的简短描述。 结果是:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

使用exception.printStackTrace来输出StackTrace。

toString()只返回异常名称和可选消息。我建议你打电话

exception.printStackTrace()

要转储消息,或者如果你需要血腥的细节:

 StackTraceElement[] trace = exception.getStackTrace()

这将输出异常,只用于调试,你应该更好地处理你的异常。

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();
}

正如您在评论中提到的,您正在使用log4j。我(无意中)发现了一个我写过东西的地方

LOG.error(exc);

而不是典型的

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

因为懒惰,或者可能只是没有想过。不幸的是,它的行为并不如您所期望的那样。记录器API实际上将Object作为第一个参数,而不是字符串——然后它对该参数调用toString()。因此,它没有得到漂亮的堆栈跟踪,只是打印出toString——在NPE的情况下,这是非常无用的。

也许这就是你正在经历的?

另一个建议——如果您正在使用Eclipse,您可以在NullPointerException本身上设置一个断点(在Debug透视图中,转到“Breakpoints”选项卡并单击有一个!在它)

检查“caught”和“uncaught”选项-现在当你触发NPE时,你会立即断点,然后你可以逐步查看它是如何被处理的,以及为什么你没有得到堆栈跟踪。

(你的问题仍然不清楚你的代码是否调用printStackTrace()或这是由日志处理程序完成的。)

以下是一些可能的解释:

  • 所使用的记录器/处理程序已配置为仅输出异常的消息字符串,而不是完整的堆栈跟踪。

  • 您的应用程序(或一些第三方库)正在使用LOG.error(ex);而不是(例如)log4j Logger方法的2参数形式记录异常。

  • 信息来自不同的地方,你认为它是;例如,它实际上是来自一些第三方库方法,或者是一些从早期调试尝试中遗留下来的随机东西。

  • 正在记录的异常重载了一些方法以模糊堆栈跟踪。如果是这种情况,异常将不是真正的NullPointerException,而是NPE的一些自定义子类型,甚至是一些未连接的异常。

我认为最后一种可能的解释是不太可能的,但人们至少会考虑做这种事情来“防止”逆向工程。当然,它只会让诚实的开发者的生活变得困难。

我们在过去看到过同样的行为。事实证明,由于某些疯狂的原因,如果NullPointerException在代码中的同一位置多次出现,使用Log.error(String, Throwable)将在一段时间后停止包含完整的堆栈跟踪。

再往前看看你的日志。你可能会找到罪魁祸首。

编辑: 这只虫子听起来相关,但它在很久以前就被修复了,这可能不是原因。

您可能正在使用HotSpot JVM(最初由Sun Microsystems开发,后来被Oracle收购,是OpenJDK的一部分),它执行大量优化。为了获得堆栈跟踪,你需要将以下选项传递给JVM:

-XX:-OmitStackTraceInFastThrow

优化是当一个异常(通常是NullPointerException)第一次发生时,打印完整的堆栈跟踪,JVM记住堆栈跟踪(或者可能只是代码的位置)。当异常发生的次数足够频繁时,堆栈跟踪就不再打印了,这既是为了获得更好的性能,也是为了不让相同的堆栈跟踪充斥日志。

要了解这是如何在HotSpot JVM中实现的,拿一份拷贝并搜索全局变量OmitStackTraceInFastThrow。上次我查看代码(在2019年)时,它在文件graphKit.cpp中。

下面是一个解释:Hotspot导致异常在生产中丢失堆栈跟踪-以及修复

我在Mac OS X上进行了测试

  • Java版本“1.6.0_26”
  • Java(TM) SE运行时环境(build 1.6.0_26-b03-383-11A511)
  • Java HotSpot(TM) 64位服务器VM (build 20.1-b02-383,混合模式)

    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...

当您在项目中使用AspectJ时,可能会发生某些方面隐藏其堆栈跟踪的部分。例如,今天我有:

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

这个堆栈跟踪是在通过Maven的surefire运行测试时打印的。

另一方面,当在IntelliJ中运行测试时,会打印不同的堆栈跟踪:

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)

VM停止输出多次抛出的异常的堆栈跟踪。这是发生在C2编译中的优化之一。

根据OpenJDK源代码,此优化应用于以下例外情况:

< p > nullpointerexception -ArithmeticException -ArrayIndexOutOfBoundsException -ArrayStoreException classcastexception < / p >