zoukankan      html  css  js  c++  java
  • 【hello,world 也打脸】记storm-starter在某知名IDE下的悲催调试经历

    背景

    最近收到这样一个问题:

    Storm处理消息时会根据Topology生成一棵消息树,Storm如何跟踪每个消息、如何保证消息不丢失以及如何实现重发消息机制?

    虽已回复,但心想还是看下storm这块的源码吧.那看静态多不爽啊,那总得调试吧,好吧,造个本地环境来调吧。

    先看看maven的build过不过:

    mvn -f pom.xml clean install
    

    搞定storm的编译打包,接着是storm-starter的编译打包,一切很顺利啊,跑一下看看:

    ${STORM_HOME}/bin/storm jar ${STORM_JAR} ${STORM_STARTER_JAR} storm.starter.WordCountTopology
    

    顺利出结果了,不就是个hello world嘛!

    接着造本地环境吧,将storm-starter的源码按maven方式导入Intellij IDEA,注意,从这时候悲催就开始了。


    hello,world 打脸了

    导入IDE后,兴致勃勃的点了F5,然后:

    这里写图片描述

    这尼玛,说好不打脸的!

    看了又看依赖“都合适”啊,ClassPath“都合适”啊,否则编译不通过啊,为毛跑!不!起!来!

    这同样的操作,在eclipse里妥妥的啊,各种能跑啊,为毛在Intellij IDEA里出错了呢?


    异常是如何产生的

    好吧,既然打脸了,又是知名IDE的粉丝,坚决要知耻而后勇的。
    那么,看下异常如何产生的吧。

    这里写图片描述

    上面的图,基本概括了异常NoClassDefFound产生的路径。

    更细节的异常产生情况如下:

    这里写图片描述

    注意看调用栈:

    • JVM_GetClassDelaredMethods是JVM内方法,在找入口函数main的过程中,此方法被调用。
    • 接着,此方法会调用验证字节码的过程:verify_code
    • 发现有用到backtype.storm.topology.IRichBolt接口,那么找找这个接口所在的.class文件吧:
      如果我们运行java -cp . xxx,通过-cp或者-classpath参数指定了classpath,那么这个.class就会被找到。然后进入parse的过程。

    • 悲催的是,IDEA的运行或者调试命令f5->run,没有将backtype/storm/topology/IRichBolt所在的jar包storm-core.jar加入classpath,这都是后话了...

    那么,既然有个找*.class的过程,这个过程如下:

    这里写图片描述

    对上图做一点简要说明:

    1. 执行java -cp . $mainClass .
    2. java程序(这里指java这个程序本身)的入口函数main,会创建虚拟机JVM实例,过程中会初始化JVM本地ClassLoader.
    3. JVM寻找.class文件时,调用ClassLoader::load_classfile方法,从jar包、zip包、目录中寻找指定的.class文件 .
    4. 本文中,木有找到backtype/storm/topology/IRichBolt.class,所以会置一个延时异常__pending_exception,这个异常关联了这个类(接口)、文件名、异常的类型(NoClassDefFound),那这个异常什么时候处理呢?后文再说.


      看下这个异常的内容:
      这里写图片描述

    异常是如何处理的

    好了,异常的产生清楚了,还有个问题,那个__pending_exception是何时被处理呢?
    看下图:

    这里写图片描述

    对上图做下简要说明:

    1. 执行java -cp . $mainClass .
    2. java程序的入口函数main,在层层初始化的过程中,会调用到LoadMainClass函数, 结合本文的第一幅图就可以知道,这个函数最终会制造那个NoClassDefFound的异常__pending_exception,然后返回的是一个空的mainClass.
    3. 紧跟着LoadMainClass函数,是一个CHECK_EXCEPTION_NULL_LEAVE的宏,这个宏展开后,会处理上面制造的那个异常,然后,打印异常信息。这里就是那个被打脸的异常了。

    为什么被打了脸

    这里,异常产生的本质和异常处理,清楚了。
    简单的概括下就是:

       /* 伪代码 */
       main /* java这个程序的main */
       -> createJVM() /* 创建JVM */
       -> loadMainClass() /* 加载我们指定的$mainClass文件,这是个class文件 */
       -> findMethod("main") /* 在$mainClass中找main方法,java写的程序的main */
       -> getMethodFromJVM() /* 没缓存,问JVM要 */
       -> classLoader.loadFromFile() /* 在classpath中找.class文件 */
       -> 没找到,置异常NoClassDefFound.
    

    但是,Intellij IDEA为何在运行时不将storm-core.jar包含进classpath呢?

    换句话说:为啥被打脸??

    这里写图片描述

    打脸的理由很简单:

    1. F5->run,先make/compile/build,再运行.
    2. 依赖的scope设为了provided,此设置仅在编译阶段将依赖的jar包加入classpath,在运行阶段,不会将jar包加入classpath.

    解决的方法也非常简单:

    如果不是通过mvn来运行,而是在IDE下调试/运行,赶紧将依赖的jar包的scope选为compile吧,妥妥的不会被打脸!

    看下是不是妥妥的呢?

    这里写图片描述


    后记

    这里写图片描述

    .

  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/foreach-break/p/dig_NoClassDefFound_Exception_inside_JVM.html
Copyright © 2011-2022 走看看