4

假设我们有以下定义:

public class Test {

    public static void main(String[] args) {
        System.out.println("------MAIN METHOD------");
        A a = new B();  
        a.a = 3;
        System.out.println(a.a);
    }

}

    public class A {

        int a;

        public void g(){
            System.out.println(a);
        }

    }

public class B extends A {}

现在,假设我们构建上述内容,然后将 B 修改为

public class B {}

并重建 JUST B。当我们关闭验证器时,该方法会打印 3!我知道验证器没有发现这一点,reslution 在 A 中检查是否有字段 a,就是这种情况......但是 B 没有为整数字段 a 分配空间!那它写到哪里呢?

4

2 回答 2

4

内存中的 java 对象被填充并对齐至少 16 个字节长,因此在仅包含 int 字段的类中存在一些未使用的空间。如果您向 A 添加更多字段,您可能会覆盖其他对象的存储空间,但这也可能不会导致立即崩溃,尤其是在立即退出的如此短的应用程序中。

通过像这样声明类,您可以享受更多乐趣:

public class A {
    int[] a = new int[1];
}

public class B {
    int a = 0x01020304;
}

public class Test {
    public static void main(String[] args) {
        A a = new B();  
        a.a[0] = 3;
        System.out.println(a.a[0]);
    }
}

通过执行此代码的结果java -Xverify:none Test应该会导致如下所示的 JVM 错误,但原则上它应该允许您将任何位置写入 java 进程的内存:

------MAIN METHOD------
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f9147d69532, pid=14785, tid=140262017775360
#
# JRE version: 6.0_26-b03
# Java VM: Java HotSpot(TM) 64-Bit Server VM (20.1-b02 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# j  Test.main([Ljava/lang/String;)V+17
于 2012-05-12T17:48:50.493 回答
0

如果你真的只重建了 B,A a = new B();应该会失败 a ClassCastException,因为 B 将不再与 A 运行时兼容。检查重建的 B 是否在运行时类路径中,这是唯一的 B 。

更正:VerifyError,不是ClassCastException

编译运行原代码: C:\TMP>%JAVA_HOME%\bin\javac -s 。*.java C:\TMP>%JAVA_HOME%\bin\java -cp 。测试------主要方法------ 3

然后更新 B.java,重新编译,再次运行: C:\TMP>%JAVA_HOME%\bin\javac -s 。B.java C:\TMP>%JAVA_HOME%\bin\java -cp 。线程“主”java.lang.VerifyError 中的测试异常:方法 Test.main([Ljava/lang/String;)V 在偏移 18 处的 putfield 中的操作数堆栈类型错误

于 2012-05-12T15:41:09.200 回答