NullPointerException丢失异常堆栈信息

时间:2022-01-19 00:44:32

在生产环境上看到tomcat/log/catalina.out一直输出异常信息,但是不见异常堆栈信息。

java.lang.NullPointerException

java.lang.NullPointerException

java.lang.NullPointerException

java.lang.NullPointerException

java.lang.NullPointerException

java.lang.NullPointerException

NullPointerException丢失异常堆栈信息

NullPointerException丢失异常堆栈信息

在使用log4j中,调用异常方法exception.getStackTrace()获取到异常堆栈信息数组StackTraceElement[],然后使用方法log.error(String msg)来循环打印StackTraceElement。(这种做法是不是很诡异-_-)。没发现任何与异常相关的信息打印出来。

NullPointerException丢失异常堆栈信息


java的标准输出在tomcat启动时,被管道重定向到了catalina.out,catalina.out信息来自所有标准输出。我们在catalina.out看到了信息,说明异常出现时,调用了标准输出,但是没有任何异常堆栈信息出来;我们在log4j配置的文件中没有找到任何异常信息说明StackTraceElement[]中没有任何信息。


最开始怀疑是log4j的使用方式有问题,导致打印不出来,但是当前的使用方式只会丢失rootCause,不会丢弃所有的异常堆栈。catalina.out中就以为是使用了标准输出打印异常类名...。事实当然不是这样,后来查看了异常处理点,基本上都会调用printStackTrace(),然后调用log4j来输出异常到其他文件。说明异常的堆栈信息确实丢失了。


异常堆栈丢失了,然后google之,*答案。从别人的回答中,可以看到,这里可能是jvm优化时,产生的结果。具体参考文章

这里自己写的代码,在接近执行两万次时,确实看到异常堆栈信息就没有了:

     
    public static void main(String[] args) { 
    	int i = 0;
    	String x= null;
    	while (i < 100000000) {
    	    try {
    	        System.out.println("当前执行次数为:"+i);
    	        getNPE(x);
    	    } catch (Exception e) {
    	    	int lth = e.getStackTrace().length;
	    		System.out.println("length:"+lth);
    	        e.printStackTrace();
    	        if(lth==0){
	    			return;
	    		}
    	    }
    	    i++;
    	}
    	
  
    } 
    
    private static void getNPE(String x){
        System.out.println("当前字母为:"+x.toString());
    }
测试时java版本信息:

java version "1.7.0_71"

Java(TM) SE Runtime Environment (build 1.7.0_71-b14)

Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

当增加虚拟机参数-XX:-OmitStackTraceInFastThrow后,执行了100w次以上,也不见异常堆栈信息丢失。

看看oracle的官方解释:

  • The compiler in the server VM now provides correct stack backtraces for all "cold" built-in exceptions. For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace. To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow.

    这里的"cold",个人以为是与hotspot VM中hot相对的意思,意思是非热点内置异常。如果异常被抛出数次,就变成”hot“了,这时就会丢失异常信息,因为这时的异常是预先分配的。

    在查找资料的时候,发现淘宝定制的vm对这个功能有个开关,可以动态切换是否禁用此项优化。

参考链接:http://www.oracle.com/technetwork/java/javase/relnotes-139183.html