Java Puzzler-44 Cutting Class,为什么我没有 NoClassDefFoundError?

Java解惑(即:Java Puzzlers: Traps, Pitfalls, and Corner Cases),作者 Joshua Bloch, Neal Gafter(Joshua Bloch就是 effective java和java concurrency in practice的作者)

第44条,为什么我就是搞不出来 NoClassDefFoundError,搜索了下,原因是 JDK6以上的 JVM的 Verifier 有了improvement(当然是好事,可以加快verification的速度。也就是,类可以更快的被加载)


从头到尾 总结下这个问题

public class Strange1 {
    public static void main(String[] args) {
        try {
            Missing m = new Missing();
        } catch (java.lang.NoClassDefFoundError ex) {
            System.out.println("Got it!");
        }
    }
}

public class Strange2 {
    public static void main(String[] args) {
        Missing m;
        try {
            m = new Missing();
        } catch (java.lang.NoClassDefFoundError ex) {
            System.out.println("Got it!");
        }
    } 
}

class Missing {
    Missing() { }
}

这三个文件编译后,手动删除 Missing.class,然后运行,会有下面的结果

JDK5以及以下:

Strange1的结果是:NoClassDefFoundError

Strange2的结果是:Got it!

看字节码来找原因


两个类都:locals[0] == args
Strange1,locals[1],即用来存储 m,也用来存储 NoClassDefFoundError(这正是抛出错误的原因)
Strange2,locals[1],只用来存储 m,local[2]用来存储 NoClassDefFoundError(m的生命周期太长,所以,m和ex不能共用同一个 local[1])

详细分析 Strange1出错的原因

JVM 进行 flow analysis 时,verifier必须合并 local[1]包含的 类型(merge the types contained in local[1])。怎么merge呢?
计算这两个类的 first common superclass,也就是:the most specific superclass they share(即,Missing 和 NoClassDefFoundError 的 first common superclass)
locals[1]的状态:
从8直接到20时:Missing
从17到20时:NoClassDefFoundError
为了 compute 这两个的 first common superclass,verifier必须 load Missing 用于决定 Missing的superclass。这正是 抛出NoClassDefFoundError 的原因(这个Error是在 verificatioin阶段抛出的)。

一定要注意:当load完 Strange1后,进行 link-verify就抛出Error啦,这个时候,甚至还不存在运行的问题。

也就是说,Strange1改成下面这个样子,也还是会报NoClassDefFoundError,这时根本就没运行

public class Strange1 {
    public static void notmain(String[] args) {
        try {
            Missing m = new Missing();
        } catch (java.lang.NoClassDefFoundError ex) {
            System.out.println("Got it!");
        }
    }
    public static void main(String[] args) {
            System.out.println("Nothing related with Missing");
    }
}

Strange2,verification时 不load Missing,等到 运行main时才 load Missing,所以,抛出的错误 就被 catch 住了


JDK6以及以上

统统都是:Got it!

在JDK1.6以及以上,是不会出现 NoClassDefFoundError 这个Error的,原因是:JDK6及以上的编译出来的类,存储了更多的 metadata

当寻找 Missing和NoClassDefFoundError的 first common superclass时,根本就无需 load Missing(从而,也不会 在verification时就抛出 NoClassDefFoundError)

所以,Strange1, Strange2 都在 运行 main 方法时才会抛出这个Error。从而,Strange1和Strange2 统统打印出 Got it

详细解释在这里:http://fupeg.blogspot.com/2007/07/java-puzzler.html(需翻墙),有意思的是:这篇文章下面,就是 Joshua Block和Neal Gafter 的comments

也可以使得 JDK6出现 NoClassDefFoundError,加一个 JVM 参数:java -XX:-UseSplitVerifier Strange1


JDK8

-XX:-UseSplitVerifier已经不支持了,即,搞不出来:NoClassDefFoundError

Java HotSpot(TM) Server VM warning: ignoring option UseSplitVerifier; support was removed in 8.0




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值