Java - java.lang.NullPointException 没有堆栈


前言

在一次日志查 bug 时发现很多空指针异常没有堆栈信息,于是乎有了这篇文章。

代码

package xianzhan.jdk8;

public class Main {

    public static void main(String[] args) {
        String s = null;
        s.toString();
    }
}

执行上述代码后控制台输出:
Exception in thread "main" java.lang.NullPointerExceptionat xianzhan.jdk8.Main.main(Main.java:7)

可能有朋友就要问了,这不就是很清楚吗,空指针异常呀,堆栈信息都提示你哪行有问题了,这还用说什么呢?如果是这么简单,到也无需说什么,接下来再看看下面这代码:

package xianzhan.jdk8;

public class Main {

    public static void main(String[] args) {
        int i = 0;
        for (; ; ) {
            try {
                i++;
                String s = null;
                s.toString();
            } catch (Exception e) {
                e.printStackTrace();
                StackTraceElement[] stackTrace = e.getStackTrace();
                if (stackTrace.length == 0) {
                    System.out.println("空指针异常次数:" + i);
                    break;
                }
            }
        }
    }
}

控制台输出:
空指针异常次数:115714
和前面代码差不多,多了 for 循环和 try catch,只不过输出到控制台的最后面,堆栈信息消失了,只剩下 java.lang.NullPointerException 这么一句信息。也就是说,当某个地方重复抛出一定次数 NullPointException,那么 JVM 可能就会丢弃该处的堆栈信息。

探究

是什么导致了后面堆栈信息没了呢?带着这样的问题查找资料,最终在 NullPointerException in Java with no StackTrace 找到答案。

首先,我们先看下导致这个问题的代码是怎样的,查看 graphKit.cpp 文件的 GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) 方法:

//------------------------------builtin_throw----------------------------------
void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) {
  bool must_throw = true;

  // If this particular condition has not yet happened at this
  // bytecode, then use the uncommon trap mechanism, and allow for
  // a future recompilation if several traps occur here.
  // If the throw is hot, try to use a more complicated inline mechanism
  // which keeps execution inside the compiled code.
  bool treat_throw_as_hot = false;
  ciMethodData* md = method()->method_data();

  if (ProfileTraps) {
    if (too_many_traps(reason)) {
      treat_throw_as_hot = true;
    }
    // (If there is no MDO at all, assume it is early in
    // execution, and that any deopts are part of the
    // startup transient, and don't need to be remembered.)

    // Also, if there is a local exception handler, treat all throws
    // as hot if there has been at least one in this method.
    if (C->trap_count(reason) != 0
        && method()->method_data()->trap_count(reason) != 0
        && has_ex_handler()) {
        treat_throw_as_hot = true;
    }
  }

  // If this throw happens frequently, an uncommon trap might cause
  // a performance pothole.  If there is a local exception handler,
  // and if this particular bytecode appears to be deoptimizing often,
  // let us handle the throw inline, with a preconstructed instance.
  // Note:   If the deopt count has blown up, the uncommon trap
  // runtime is going to flush this nmethod, not matter what.
  if (treat_throw_as_hot
      && (!StackTraceInThrowable || OmitStackTraceInFastThrow)) {
    // If the throw is local, we use a pre-existing instance and
    // punt on the backtrace.  This would lead to a missing backtrace
    // (a repeat of 4292742) if the backtrace object is ever asked
    // for its backtrace.
    // Fixing this remaining case of 4292742 requires some flavor of
    // escape analysis.  Leave that for the future.
    ciInstance* ex_obj = NULL;
    switch (reason) {
    case Deoptimization::Reason_null_check:
      ex_obj = env()->NullPointerException_instance();
      break;
    case Deoptimization::Reason_div0_check:
      ex_obj = env()->ArithmeticException_instance();
      break;
    case Deoptimization::Reason_range_check:
      ex_obj = env()->ArrayIndexOutOfBoundsException_instance();
      break;
    case Deoptimization::Reason_class_check:
      if (java_bc() == Bytecodes::_aastore) {
        ex_obj = env()->ArrayStoreException_instance();
      } else {
        ex_obj = env()->ClassCastException_instance();
      }
      break;
    default:
      break;
    }
    if (failing()) { stop(); return; }  // exception allocation might fail
    if (ex_obj != NULL) {
      if (env()->jvmti_can_post_on_exceptions()) {
        // check if we must post exception events, take uncommon trap if so
        uncommon_trap_if_should_post_on_exceptions(reason, must_throw);
        // here if should_post_on_exceptions is false
        // continue on with the normal codegen
      }

      // Cheat with a preallocated exception object.
      if (C->log() != NULL)
        C->log()->elem("hot_throw preallocated='1' reason='%s'",
                       Deoptimization::trap_reason_name(reason));
      const TypeInstPtr* ex_con  = TypeInstPtr::make(ex_obj);
      Node*              ex_node = _gvn.transform(ConNode::make(ex_con));

      // Clear the detail message of the preallocated exception object.
      // Weblogic sometimes mutates the detail message of exceptions
      // using reflection.
      int offset = java_lang_Throwable::get_detailMessage_offset();
      const TypePtr* adr_typ = ex_con->add_offset(offset);

      Node *adr = basic_plus_adr(ex_node, ex_node, offset);
      const TypeOopPtr* val_type = TypeOopPtr::make_from_klass(env()->String_klass());
      Node *store = access_store_at(ex_node, adr, adr_typ, null(), val_type, T_OBJECT, IN_HEAP);

      add_exception_state(make_exception_state(ex_node));
      return;
    }
  }

  // %%% Maybe add entry to OptoRuntime which directly throws the exc.?
  // It won't be much cheaper than bailing to the interp., since we'll
  // have to pass up all the debug-info, and the runtime will have to
  // create the stack trace.

  // Usual case:  Bail to interpreter.
  // Reserve the right to recompile if we haven't seen anything yet.

  ciMethod* m = Deoptimization::reason_is_speculate(reason) ? C->method() : NULL;
  Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile;
  if (treat_throw_as_hot
      && (method()->method_data()->trap_recompiled_at(bci(), m)
          || C->too_many_traps(reason))) {
    // We cannot afford to take more traps here.  Suffer in the interpreter.
    if (C->log() != NULL)
      C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'",
                     Deoptimization::trap_reason_name(reason),
                     C->trap_count(reason));
    action = Deoptimization::Action_none;
  }

  // "must_throw" prunes the JVM state to include only the stack, if there
  // are no local exception handlers.  This should cut down on register
  // allocation time and code size, by drastically reducing the number
  // of in-edges on the call to the uncommon trap.

  uncommon_trap(reason, action, (ciKlass*)NULL, (char*)NULL, must_throw);
}

从上面代码可以知道,JVM 对特定异常做了优化:

  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ArrayStoreException
  • ClassCastException

当 JVM 检测到某个位置连续多次抛出同一个异常,到达某个数量的时候,就会抛出 JVM 内的一个默认的异常,该异常是 env()->NullPointerException_instance(); 指向的数据,无需堆栈,因此速度会很快,也就是说,该操作是为了优化性能而做的一个优化。

OmitStackTraceInFastThrow

上面的优化是因为在启动服务时,JVM 默认添加了 -XX:+OmitStackTraceInFastThrow 参数,我们可以使用 java -XX:+PrintFlagsFinal 查看是否添加。
OmitStackTraceInFastThrow

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java测试题3》<br><br>一、 是非题5题(每题3分)<br>1、 Java是一种计算机语言,更是一种计算机技术。 ( )<br>2、 Java支持多继承。 ( )<br>3、 接口是特殊的抽象类。 ( )<br>4、Java数据库连接用的是ODBC。 ( )<br>5、封装的原则是用公有的方法访问私有的数据 ( )<br>二、 填空题(每空2分)<br>6、Java可以跨平台的原因是( )<br>7、方法签名(或者特征)是指方法名和( )、( )、( )<br>8、Java事件处理包括建立事件源、( )和( )<br>9、JavaBeans的属性分为简单属性、( )、( )、( )。<br>10、Jsp指令的作用是( )<br>三、 程序题(每题5分,有单选和多选)<br>11、下面属于Java 关键字的是<br>A、 NULL B、IF C、do D、go to <br>12. 哪一个输出-4.0<br>A、 System.out.println(Math.floor(-4.7));<br>B、 System.out.println(Math.round(-4.7));<br>C、 System.out.println(Math.ceil (-4.7));<br>D、 System.out.println(Math.min(-4.7));<br>13、下例正确的句子是<br>A)float 3.14;<br>B)byte i=225;<br>C)log k=33;<br>D)int p[][];<br><br>14、给出:<br>public class foo{<br>public static void main (String[]args){<br>String s;<br>System.out.println(“s=”+s);<br>}<br>}<br>what is the result?<br>A、The code compiles and “s=” is printed<br>B 、The code complies and “s=null” is printed<br>C 、The code does not compile because string s is not initialized<br>D 、The code does not compile because string s can not be referenced<br>E 、The code compiles, but a NullPointException is thrown when tostring is called<br><br>15、给出:<br>8.int index=1;<br>9.int []foo=new int[3];<br>10.int bar=foo[index];<br>11.int baz=bar+index;<br>What is the result?<br>A、baz has the value of 0<br>B、baz has the value of 1<br>C、baz has the value of 2<br>D、an exception is thrown<br>E、 the code will not compile<br><br>16.下例操作的结果是什么?<br>System.out prinln(4|3);<br>1)6<br>2)0<br>3)1<br>4)7<br><br>17、哪一个不能被增加到容器?<br>A、 a Panel<br>B、 an Applet<br>C、a Component<br>D、a top Container<br>E、a MenuItem<br><br>18、给出:<br>class Test {<br>int i;<br>String s;<br>public void method(){<br>int i=10;<br>system.out.println(i);<br>public Test(){<br>system.out.println(s);<br>}<br>}<br>What is the result?<br><br>19、public class ko3_6{<br>static{<br>System.out.println(“Hello”);<br>}<br>}<br><br>What is the result?<br>20、public class ko5_8{<br>public static void main(String args[]) {<br>int x=1,sum=0;<br>while(x<=10){<br>sum+=x;<br>x++;<br>}<br>System.out.println("sum="+sum);<br>}<br>}<br><br>What is the result?<br><br>21、 public class ko6_9<br>{<br>public static void main(String args[]) <br>{<br>int sum=0;<br>int ko[][]={{1,2,3},{4,5,6},{7,8,9}};<br>for(int n=0;n<3;n++)<br>for(int m=0;m<3;m++)<br>sum+=ko[n][m];<br>System.out.println("sum="+sum);<br>}<br>}<br><br>What is the result?<br><br>22、public class ko8_1<br>{<br>public static void main(String args[])<br>{<br>try<br>{ <br>int x[]=new int[-5];<br>System.out.println("此行将无法被执行!");<br>}<br>catch(NegativeArraySizeException e)<br>{<br>System.out.println("exception: " + e.getMessage());<br>}<br>}<br>}<br><br>What is the result?<br>23、 public class ko10_1 extends Thread<br>{ <br>int n;<br>ko10_1()<br>{<br>Thread td=new Thread(this);<br>td.start();<br>}<br>public void run()<br>{<br>for (n=0;n<6;n++)<br>{<br>try<br>{<br>System.out.print(n);<br>Thread.sleep(500);<br>}<br>catch(InterruptedException e)<br>{<br>System.out.println("Exception");<br>}<br>}<br>}<br>public static void main(String args[])<br>{<br>new ko10_1();<br>}<br>}<br>What is the result?<br><br><br>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值