更改 object header 中的类指针
Changing the klass pointer in an object header
我试图将 object 的 klass 指针更改为指向具有相同设置的不同 class。准确地说,对于我的测试,我使用了原始 class 的副本和修改后的 toString()
方法,只是为了打印出其他内容。
假设 JVM 在内存中以相同的方式对属性进行排序 object 两个 classes 应该看起来相同。
所以在我的测试中,我从新 class 的 object 获得了 klass 指针,并设置在旧的原始 class 的 object 上。调用 toString()
后,我看到了预期的新输出。
但是,当我在循环中执行此操作时,JVM 崩溃了。我尝试创建 new Test()
objects 并将 klass 指针修改为指向 Test2
像这样(注意:64 位压缩 OOP):
int test2KlassIdentifier = unsafe.getInt(test2Obj, 8L);
unsafe.putInt(testObj, 8L, test2KlassIdentifier);
在创建了数十万个 object 之后,我得到了一个核心转储:
# 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 相关的问题,我的更改在内部造成了一些混乱。但是,我想了解我的 "patched" object 与新创建的 object 类型 Test2
有何不同
不要试图愚弄 JVM。这样的实验几乎注定要失败。
在这种特殊情况下,JIT 编译器拒绝在偏移量 #8 处执行 'regular' 加载操作,因为它假定只允许 LoadKlassNode
读取klass_offset。但是还有许多其他原因导致此类技巧可能导致 JVM 崩溃。
即使这个技巧有时在解释代码中有效,它也可能在 JIT 编译后失败,因为 object 的 class 的概念不仅仅是 object header。如果您尝试在不同的实例上调用它,为一个特定 class 生成的机器代码将变得无效:想想指令流中的绝对地址等
另请注意,当您更改 header 时,classes 可能具有不兼容的状态,例如它们有不同状态的常量池缓存和 JVM 懒惰填充的其他结构。
我试图将 object 的 klass 指针更改为指向具有相同设置的不同 class。准确地说,对于我的测试,我使用了原始 class 的副本和修改后的 toString()
方法,只是为了打印出其他内容。
假设 JVM 在内存中以相同的方式对属性进行排序 object 两个 classes 应该看起来相同。
所以在我的测试中,我从新 class 的 object 获得了 klass 指针,并设置在旧的原始 class 的 object 上。调用 toString()
后,我看到了预期的新输出。
但是,当我在循环中执行此操作时,JVM 崩溃了。我尝试创建 new Test()
objects 并将 klass 指针修改为指向 Test2
像这样(注意:64 位压缩 OOP):
int test2KlassIdentifier = unsafe.getInt(test2Obj, 8L);
unsafe.putInt(testObj, 8L, test2KlassIdentifier);
在创建了数十万个 object 之后,我得到了一个核心转储:
# 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 相关的问题,我的更改在内部造成了一些混乱。但是,我想了解我的 "patched" object 与新创建的 object 类型 Test2
不要试图愚弄 JVM。这样的实验几乎注定要失败。
在这种特殊情况下,JIT 编译器拒绝在偏移量 #8 处执行 'regular' 加载操作,因为它假定只允许 LoadKlassNode
读取klass_offset。但是还有许多其他原因导致此类技巧可能导致 JVM 崩溃。
即使这个技巧有时在解释代码中有效,它也可能在 JIT 编译后失败,因为 object 的 class 的概念不仅仅是 object header。如果您尝试在不同的实例上调用它,为一个特定 class 生成的机器代码将变得无效:想想指令流中的绝对地址等
另请注意,当您更改 header 时,classes 可能具有不兼容的状态,例如它们有不同状态的常量池缓存和 JVM 懒惰填充的其他结构。