[娱乐] [JVM] 试试在32位HotSpot上跑这个?
RednaxelaFX
2010-02-12
上周某晚睡觉前觉得无聊,写了同样无聊的一段测试代码来解闷。都怪MVEL让我看到了奇怪的代码……
本来想发一帖讲解原理的,上周偏偏又加班没来得及。于是干脆先把代码扔在这里好了。回头再讲解“为什么”。欢迎回复讨论 ^_^ 这个代码测试了在32位Java 6的HotSpot VM上可以跑,在Windows Vista SP1、Windows XP SP3和某版本32位Linux上跑都没问题。64位HotSpot的话肯定不行,HotSpot之外别的JVM也不行,因为涉及到HotSpot当前实现中使用的对象布局细节,事先提醒。 另外这段代码是非常不安全的,它之所以能显示出我想要的结果是因为程序执行中没有引发过GC。当前HotSpot的年轻代堆空间都采用了基于copy的GC算法,会移动对象;而下面的代码的“成功”依赖于对象地址保持稳定。 import java.lang.reflect.Field; import sun.misc.Unsafe; class IntValueHolder { public int value; @Override public String toString() { return String.format("IntValueHolder: 0x%X", this.value); } } class ReferenceHolder { public Object value; @Override public String toString() { return String.format("ReferenceHolder: %s", this.value); } } public class TestUnsafe { public static void test() throws Throwable { ReferenceHolder refHolder = new ReferenceHolder(); refHolder.value = "Hello, unsafe!"; Unsafe unsafe = getUnsafe(); Field valueField = ReferenceHolder.class.getField("value"); long valueFieldOffset = unsafe.objectFieldOffset(valueField); long objAddr = unsafe.getInt(refHolder, valueFieldOffset); System.out.println("object at: 0x" + Long.toString(objAddr & 0x0FFFFFFFF, 16).toUpperCase()); long strlenAddr = objAddr + 16; System.out.println("string length: " + unsafe.getInt(null, strlenAddr)); int newStringLength = 16; unsafe.putInt(null, strlenAddr, newStringLength); long charArrAddrAddr = objAddr + 8; long charArrAddr = unsafe.getInt(null, charArrAddrAddr); long charArrLenAddr = charArrAddr + 8; unsafe.putInt(null, charArrLenAddr, newStringLength); unsafe.putLong(null, charArrAddr + 28, 0x006F00630069006EL); unsafe.putLong(null, charArrAddr + 36, 0x00580046006E0072L); IntValueHolder intHolder = new IntValueHolder(); intHolder.value = (int) objAddr; Object obj = unsafe.getObject(intHolder, valueFieldOffset); System.out.println(obj); long klassOffset = 4; int refHolderKlass = unsafe.getInt(refHolder, klassOffset); System.out.println(intHolder.toString()); unsafe.putInt(intHolder, klassOffset, refHolderKlass); System.out.println(intHolder.toString()); ReferenceHolder fakeRefHolder = (ReferenceHolder)(Object)intHolder; System.out.println(fakeRefHolder.value); } public static Unsafe getUnsafe() throws Throwable { Class<?> unsafeClass = Unsafe.class; for (Field f : unsafeClass.getDeclaredFields()) { if ("theUnsafe".equals(f.getName())) { f.setAccessible(true); return (Unsafe) f.get(null); } } return null; } public static void main(String[] args) throws Throwable { test(); System.out.println("Done testing."); } } 编译时会有警告,那些是关于使用了Sun的私有API的警告,可以忽略。 在我的装着Vista的笔记本上跑的其中一次结果是: 引用 object at: 0x27C79F40
string length: 14 Hello, unicornFX IntValueHolder: 0x27C79F40 ReferenceHolder: Hello, unicornFX Hello, unicornFX Done testing. 值得留意的几个地方: 1、我在源码里写的字符串内容是"Hello, unsafe!",长度为14字符;但输出出来的字符串内容是"Hello, unicornFX",长度是16字符; 2、第52与行第54行的代码完全一样,局部变量intHolder的值也没变过,但输出的结果却不同; 3、第55行在完全没继承关系的IntValueHolder与ReferenceHolder之间成功进行了引用的类型转换而没有抛ClassCastException。 Have fun ^_^ (But don't try this in production.) |
|
kyfxbl
2011-01-06
没有sun.misc包,跑不了,这个包在哪里可以得到呢?
另外,很奇怪为什么会有类的名字叫Unsafe,呵呵,这个类的意义何在?是专门用来测试的吗? |
|
RednaxelaFX
2011-01-06
在rt.jar里,不需要额外写classpath参数。遇到的错误具体是什么?
这个类是JDK内部一些功能使用的。例如说AtomicXXX用到了Unsafe里的compareAndSwapXXX方法,DirectByteBuffer用了Unsafe的memory系方法,反射的一小块用了allocateInstance方法,等等。这个类不是给外部用的,也不是公有API的一部分。如果编译的源码里有用到它的话,一定会被javac警告。 在Eclipse里的话要把项目里的Java编译器警告选项中使用内部API的限制关掉才行。 |
|
Willam2004
2011-02-09
unsafe.putLong(null, charArrAddr + 28, 0x006F00630069006EL); unsafe.putLong(null, charArrAddr + 36, 0x00580046006E0072L); 是这两句的影响导致输出FX么?不知道LZ是怎么算出来像0x006F00630069006EL和0x00580046006E0072L的数字的? |
|
RednaxelaFX
2011-02-09
简短回答:
0x006F00630069006EL是"nico",0x00580046006E0072L是"rnFX"。 由于Java的内部字符编码使用UTF-16BE,而x86是little-endian的,所以这么做。 至于详细回答…楼上要是有兴趣的话我再详细说 |
|
Willam2004
2011-02-10
RednaxelaFX 写道 简短回答:
0x006F00630069006EL是"nico",0x00580046006E0072L是"rnFX"。 由于Java的内部字符编码使用UTF-16BE,而x86是little-endian的,所以这么做。 至于详细回答…楼上要是有兴趣的话我再详细说 呵呵,我是挺有兴趣,但我估计需要先准备些基础知识。不然也理解不了。 |
|
Willam2004
2011-02-25
Unsafe 这个类有啥用?lz能给些资料看看不?或者解释下?
|
|
qz小峰
2011-02-25
Willam2004 写道 Unsafe 这个类有啥用?lz能给些资料看看不?或者解释下?
直接源码,各个方法都有详细的描述.通常是一些底层的操作. |
|
RednaxelaFX
2011-02-25
Willam2004 写道 Unsafe 这个类有啥用?lz能给些资料看看不?或者解释下?
RednaxelaFX 写道 在rt.jar里,不需要额外写classpath参数。遇到的错误具体是什么?
这个类是JDK内部一些功能使用的。例如说AtomicXXX用到了Unsafe里的compareAndSwapXXX方法,DirectByteBuffer用了Unsafe的memory系方法,反射的一小块用了allocateInstance方法,等等。这个类不是给外部用的,也不是公有API的一部分。如果编译的源码里有用到它的话,一定会被javac警告。 在Eclipse里的话要把项目里的Java编译器警告选项中使用内部API的限制关掉才行。 这段就是在说Unsafe啊…资料的话如楼上所说,读源码就好了,里面几乎全都是native方法所以在Java这层也没啥可说的。 Unsafe.java unsafe.cpp |
相关讨论
相关资源推荐
- 用4条ADSL组建一个局域网的方法介绍(图文教程)
- 项目:小型局域网的搭建(基础知识+案例)
- 组建小型局域网思路
- 如何知道Java虚拟机JVM是64位JVM还是32位?
- 32位java虚拟机_如何知道Java虚拟机JVM是64位JVM还是32位? - Break易站
- mac Error attaching to process sun.jvm.hotspot. Can‘t attach symbolicator to the process
- 不要再死记硬背了,你试试这样理解JVM的这几代?
- mac Error attaching to process: sun.jvm.hotspot.: Can‘t attach symbolicator to the process
- java jar 设置jvm参数_请问该如何设置Java虚拟机JVM启动内存参数?
- 线程崩了,为什么不会导致 JVM 崩溃呢?如果是主线程呢?