java - 对字节码和对象的澄清

标签 java jvm object bytecode verification

我正在编写一个字节码工具。现在,我正试图找出如何在存在物体的情况下做到这一点。我想对我在 JVMS(第 4.9.4 节)中阅读的两行内容进行一些说明:

1) "The verifier rejects code that uses the new object before it has been initialized."

我的问题是,这里的“使用”是什么意思?我猜这意味着:将其作为方法属性传递,调用 GETFIELDPUTFIELD在其上,或在其上调用任何实例方法。他们的其他禁止用途是什么?而且我相信它遵循其他指令,例如 DUP , LOADSTORE是允许的。

2) "Before that method invokes another instance initialization method of myClass or its direct superclass on this, the only operation the method can perform on this is assigning fields declared within myClass."

这意味着在 <init>方法,GETFIELD 和 PUTFIELD 在另一个之前被允许 <init>叫做。但是,在 Java 中,在调用 super() 之前对实例字段执行任何操作或 this()导致编译错误。有人可以澄清一下吗?

3) 我还有一个问题。对象引用何时被初始化,并因此准备好被自由使用?通过阅读 JVMS,我得出了一个答案,即是否初始化一个对象取决于每个方法。在某个时间点,可以为一个方法初始化对象,但不能为另一个方法初始化对象。具体来说,对象在 <init> 时为方法初始化。由该方法调用返回。

例如,考虑 main()方法创建了一个对象并调用了 <init>然后调用父类(super class)的 <init> .从super()回来后, 该对象现在被认为由 <init> 初始化, 但尚未为 main() 初始化.这是否意味着,在 <init>super() 之后,我可以将对象作为参数传递给方法,甚至在从 main() 返回之前。

有人可以确认整个分析是真实的吗?感谢您的时间。

ps:我实际上已经在 Sun 论坛上发布了同样的问题,但没有回应。我希望我能在这里有更多的运气。谢谢。

更新

首先感谢您的回答和时间。虽然我没有得到明确的答案(我有很多问题,其中一些有点模糊),但是您的答案和示例以及随后的实验对我更深入地了解 JVM 的工作原理非常有用。

我发现的主要事情是 Verifier 的行为随着不同的实现和版本而不同(这使得字节码操作的工作变得更加复杂)。问题在于不符合 JVMS,或者缺乏来自验证者开发人员的文档,或者 JVMS 在验证者领域有一些微妙的模糊。

最后一件事,太棒了!!!我在 Sun JVM Specifications 官方论坛上发了同样的问题,到现在还是没有答案。

最佳答案

与 java 语言指定的相反,在字节码级别,可以在调用父类(super class)构造函数之前访问构造函数中的类的字段。下面的代码使用 asm 库来创建这样一个类:

package asmconstructortest;

import java.io.FileOutputStream;
import org.objectweb.asm.*;
import org.objectweb.asm.util.CheckClassAdapter;
import static org.objectweb.asm.Opcodes.*;

public class Main {

    public static void main(String[] args) throws Exception {
        //ASMifierClassVisitor.main(new String[]{"/Temp/Source/asmconstructortest/build/classes/asmconstructortest/Test.class"});
        ClassWriter cw = new ClassWriter(0);
        CheckClassAdapter ca = new CheckClassAdapter(cw);

        ca.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "asmconstructortest/Test2", null, "java/lang/Object", null);

        {
            FieldVisitor fv = ca.visitField(ACC_PUBLIC, "property", "I", null, null);
            fv.visitEnd();
        }

        {
            MethodVisitor mv = ca.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitInsn(ICONST_1);
            mv.visitFieldInsn(PUTFIELD, "asmconstructortest/Test2", "property", "I");
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(2, 1);
            mv.visitEnd();
        }

        ca.visitEnd();

        FileOutputStream out = new FileOutputStream("/Temp/Source/asmconstructortest/build/classes/asmconstructortest/Test2.class");
        out.write(cw.toByteArray());
        out.close();
    }
}

实例化这个类工作正常,没有任何验证错误:

package asmconstructortest;

public class Main2 {
    public static void main(String[] args) {
        Test2 test2 = new Test2();
        System.out.println(test2.property);
    }
}

关于java - 对字节码和对象的澄清,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3278865/

相关文章:

java - 流程,如何使用Outputstream发送两次消息

java - 菜单程序中的可变错误

Java CMS GC,系统空闲时GC线程占用CPU

hadoop - 覆盖默认的JVM重用值

php - 使用 simplexml 解析时如何忽略大小写?

用于蜜蜂计数的 Matlab Cascade 训练

c++ - C++中,为什么返回不同对象私有(private)变量的指针会导致段错误?

java - 如何在 Java 中对 long 进行 mod2^64?

java - 数组中多个最频繁出现的元素

java - 如何在不退出 JVM 的情况下多次运行 Java 程序?