【问题标题】:Understanding unitialized objects in Java理解 Java 中未初始化的对象
【发布时间】:2018-03-19 13:46:25
【问题描述】:

我试图了解在 Java 中如何将引用存储到 Object[] 中。所以我生成了一个ClassASM 库,如下所示:

public static Class<?> getKlass(){
    String className = "TestClass";
    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);

    classWriter.visit(V1_8, ACC_PUBLIC, className, null, getInternalName(Object.class), null);

    MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC + ACC_STATIC, "m", "()[Ljava/lang/Object;",null, null);
    //ARRAY_SIZE
    mv.visitInsn(ICONST_1);
    mv.visitTypeInsn(ANEWARRAY, getInternalName(Object.class));
    mv.visitInsn(DUP);
    mv.visitInsn(ICONST_0);
    mv.visitTypeInsn(NEW, getInternalName(Object.class));
    mv.visitInsn(AASTORE);  // <--- Here is the problem
    mv.visitInsn(ARETURN);
    mv.visitMaxs(1, 1);
    mv.visitEnd();

    return new ByteArrayClassLoader().defineClass(classWriter.toByteArray());
}

当我运行这段代码时,JVM 用java.lang.VerifyError 发出信号

Type uninitialized 6 (current frame, stack[3]) is not assignable to 'java/lang/Object'

添加构造函数调用按预期工作

mv.visitTypeInsn(NEW, getInternalName(Object.class));
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, getInternalName(Object.class), "<init>", "()V", false);
mv.visitInsn(AASTORE);

现在它工作正常。但行为尚不清楚。我希望未初始化和已初始化的对象具有相同的类型。正如JVMS 中所指定的,我们没有特殊的未初始化类型。所以我希望 JVM 允许加载带有未初始化引用的类。

我错过了什么?

【问题讨论】:

    标签: java class object jvm


    【解决方案1】:

    有一个未初始化的类型,但它只存在于验证者中,resp。在应该帮助验证者的堆栈映射表条目中。由于包含NEW 指令的方法严格要求必须通过invokespecial 调用构造函数,并且必须在使用对象之前调用它,因此无需在通用类型系统中对其进行建模。一旦验证者确保每个方法都正确初始化对象,您就不会在不适当的地方遇到未初始化的对象。

    §4.10.1.2. Verification Type System:

         Verification type hierarchy:
    
                                      top
                          ____________/\____________
                         /                          \
                        /                            \
                     oneWord                       twoWord
                    /   |   \                     /       \
                   /    |    \                   /         \
                 int  float  reference        long        double
                              /     \
                             /       \_____________
                            /                      \
                           /                        \
                    uninitialized                    +------------------+
                     /         \                     |  Java reference  |
                    /           \                    |  type hierarchy  |
         uninitializedThis  uninitialized(Offset)    +------------------+  
                                                              |
                                                              |
                                                             null
    

    uninitializedThis 是构造函数中this 引用的类型,在调用超类构造函数之前。 uninitialized(Offset) 是在调用构造函数之前由NEW 创建的实例的类型。 “Offset”是指NEW指令在方法字节码中的指令偏移量。这允许跟踪可能飞来飞去的多个未初始化实例,例如在做new Foo(new Bar(new Baz())) 甚至new Foo(new Foo(new Foo())) 时。正如this answer 中所述,将未初始化的实例绑定到创建NEW 指令会对代码结构施加一些限制,例如您不能合并不同NEW指令的未初始化类型,即使它们在调用构造函数后具有相同的引用类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多