更改对象标头中的klass指针

时间:2018-05-15 11:30:57

标签: java garbage-collection jvm unsafe

我尝试将对象的klass指针更改为指向具有相同设置的其他类。确切地说,对于我的测试,我使用原始类的副本和修改后的toString()方法,只是打印出其他内容。

假设JVM以相同的方式命令属性,两个类的内存对象应该看起来相同。

因此,在我的测试中,我从新类的对象中获取了klass指针,并设置了旧的原始类的对象。在调用toString()之后,我按预期看到了新输出。

然而,当我在循环中执行此操作时,JVM崩溃了。我尝试创建new Test()个对象,并将klass指针修改为指向Test2,如下所示(注意:64位压缩OOP):

int test2KlassIdentifier = unsafe.getInt(test2Obj, 8L);
unsafe.putInt(testObj, 8L, test2KlassIdentifier);

创建了数十万个对象后,我得到了一个核心转储:

#  Internal Error (C:\ojdkbuild\lookaside\java-1.8.0-openjdk\hotspot\src\share\vm\opto\memnode.cpp:906), pid=27120, tid=0x0000000000009374
#  assert(!(adr_type->isa_oopptr() && adr_type->offset() == oopDesc::klass_offset_in_bytes())) failed: use LoadKlassNode instead

然后我将数字减少到只创建100.000 - >在我之后创建了一堆new Object()之前,没有核心转储。

所以我的感觉是,这是一个与GC有关的问题,而且我的改变在内部混淆了一些东西。但是,我想了解我的“修补”对象与新创建的Test2类型对象的不同之处

1 个答案:

答案 0 :(得分:2)

不要试图欺骗JVM。这样的实验几乎总是注定要失败。

在这种特殊情况下,JIT编译器在偏移#8处拒绝'常规'加载操作,因为它假定只允许LoadKlassNode在klass_offset读取。但是还有很多其他原因导致JVM崩溃。

即使这个技巧有时在解释代码中有效,但在JIT编译之后很可能会失败,因为对象类的概念不仅仅是对象头中的引用。如果您尝试在另一个实例上调用它,则为一个特定类生成的机器代码将变为无效:考虑指令流中的绝对地址等。

另外请注意,更改标题时类可能会出现不兼容的状态,例如:它们具有不同的常量池缓存状态以及JVM可能会延迟填充的其他结构。