假设我们有以下定义:
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!据我所知,验证者没有发现这一点,A中的洗脱检查有一个字段a,情况就是这样......但是B没有为整数字段分配空间a!它写在哪里呢?
答案 0 :(得分: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
答案 1 :(得分:0)
如果你真的只重建了B,A a = new B();
应该失败并且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。测试 线程“main”中的异常java.lang.VerifyError:方法Test.main中的putfield中操作数堆栈上的错误类型([Ljava / lang / String;)V偏移量为18