JVM超级构造函数调用

时间:2015-02-17 19:12:42

标签: java constructor jvm bytecode-manipulation

使用JVM字节码的编译器,我注意到一些没有多大意义的构造函数:

每个Java类的每个构造函数都调用super构造函数,甚至是Object的直接子类。这是由Java编译器强制执行的(当它无法在构造函数的开头隐式添加调用时)和Bytecode Verifier强制执行,如使用自定义.class文件的此错误所示:

java.lang.VerifyError: Constructor must call super() or this() before return
Exception Details:
  Location:
    dyvil/test/Main.<init>()V @0: return
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: b1

对于某些带有字段等的随机类的子类,这是有意义的,因为这些字段必须初始化,但是在Object的子类的情况下,这是非常< / em>对nop方法的公共调用(+方法调用开销)。这引出了一些问题:

  1. 为什么JVM强制执行此行为?
  2. 超类在子类之前加载/初始化是否重要?在调用构造函数之前(在子类初始化时)不能确保这一点吗?
  3. Object的情况下,该类应该已经在任何给定点加载,那么为什么需要调用它的构造函数呢?
  4. 这个无用的调用是否被JIT编译器优化了?
  5. 编辑:Object构造函数的字节码

    public void <init>()
       L0
        LINENUMBER 37 L0
        RETURN
        MAXSTACK = 0
        MAXLOCALS = 1
    

    正如您可能看到的,此构造函数根本不执行任何操作。

2 个答案:

答案 0 :(得分:2)

首先,RETURN中的单个Object.<init>并不意味着Object的构造函数是NOP。

Object类通常是JVM内部函数,JVM比它的字节码更了解它。例如,HotSpot使用Object.<init> to register finalizers

关于Object.<init>的最好的事情是 - 它是任何实例分配不可避免地要求的唯一方法。看看它是一个强大的功能:你可以在Object.<init>上设置一个断点,一次拦截所有构造函数。您还可以使用Instrumentation API修改Object的构造函数以跟踪所有分配,依此类推......

关于性能 - 是的,JIT编译器在不需要时消除了不必要的方法调用,例如:当这个方法没有断点时,没有finalize等。对于其他琐碎的方法也是如此,不仅仅是Object.<init>。所以它们没有性能影响。

答案 1 :(得分:1)

  1. 除了没有超类之外,Object是一个类似于其他类的类。它有一个必须被调用的构造函数。 JVM规范并不排除Object在其实例初始化方法中有指令。
  2. 这是因为静态初始值设定项在加载类时执行(yes, you can avoid that if you load the class manually)。即使它们是在实例化时调用的,也指定层次结构在加载时必须保持一致。有时规范是你唯一的理由。
  3. 加载类和创建实例是两回事。实际上,Hotspot会在VM启动时加载来自java.lang的所有类(如果我没记错的话,我已经读了很长时间了)。另见1.
  4. 我不知道,但如果我没有忽视任何事情,我想这是可能的。我非常怀疑在任何情况下都花了很多时间来调用Object的构造函数。如果您认为:测量!