很多低级语言中可用的技巧在Java中都是不被允许的。Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的。我们知道JAVA作为高级语言的重要创新一点就是在于JVM的内存管理功能,这完全区别于C语言开发过程中需要对变量的内存分配小心控制,JVM很大程度解放了码农对于内存的调整。
一直以来,JAVA在大多数人心目中没有办法对内存进行操作的,其实不然,Unsafe
类就是一把操作JAVA内存的钥匙。如果你想搞破坏,可以使用Unsafe这个类。这个类是属于sun.* API中的类。
Unsafe做操作的是直接内存区,所以该类没有办法通过HotSpot的GC进行回收,需要进行手动回收,因此在使用此类时需要注意内存泄漏(Memory Leak)和内存溢出(Out Of Memory)。
因为这是一个平台相关的类,因此在实际开发中,建议不要使用。
1、实例化sun.misc.Unsafe
Unsafe类提供了一个静态方法getUnsafe()来获取Unsafe的实例,但是如果你尝试调用Unsafe.getUnsafe(),会得到一个SecutiryException。这个类只有被JDK信任的类实例化。
但是这总会是有变通的解决办法的,一个简单的方式就是使用反射进行实例化:
Field f = Unsafe.class.getDeclaredField("theUnsafe"); // Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null);
2、Unsafe内存操作案例
设置基本数据类型变量的值:
public class UnsafeDemo { private int i = 0; public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { //获取Unsafe实例 Field f = Unsafe.class.getDeclaredField("theUnsafe"); // Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); //获取字段i在内存中偏移量 long offset = unsafe.objectFieldOffset(UnsafeDemo.class.getDeclaredField("i")); //创建对象实例,设置字段的值 UnsafeDemo unsafeDemo = new UnsafeDemo(); unsafe.putInt(unsafeDemo, offset, 100); //打印结果 System.out.println(unsafeDemo.i); } }
上述代码输出:
100 |
注意在这个案例中,我们不是直接给int型变量i赋值,而是通过调用以下方法进行赋值:
unsafe.putInt(unsafeDemo, offset, 100);
其中offset是表示的是i在内存中的偏移量。何谓偏移量?
JVM的实现可以自由选择如何实现Java对象的“布局”,也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之类。sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来,它提供了objectFieldOffset()方法用于获取某个字段相对Java对象的“起始地址”的偏移量,也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个Java对象的某个字段。
在上例中,我们通过putInt方法给一个int变量i赋值,类似的,Unsafe也提供了putLong、putFloat、putDouble、putChar、putByte、putShort、putBoolean、以及putObject等方法给对应类型的变量赋值。并提供了相应的get方法。
突破限制创建实例
通过allocateInstance()方法,你可以创建一个类的实例,但是却不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例。
(这个对单例模式
情有独钟的程序员来说将会是一个噩梦,它们没有办法阻止这种方式调用)
看下面一个实例(注:为了配合这个主题,译者将原实例中的public构造函数修改为了私有的):
public class UnsafeDemo { public static void main(String[] args) throws Exception{ Field f = Unsafe.class.getDeclaredField(theUnsafe); // Internal reference f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); // This creates an instance of player class without any initialization Player p = (Player) unsafe.allocateInstance(Player.class); System.out.println(p.getAge()); // Print 0 p.setAge(45); // Let's now set age 45 to un-initialized object System.out.println(p.getAge()); // Print 45 } } class Player { private int age = 12; private Player() { this.age = 50; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } }
总结sun.misc.Unsafe提供了可以随意查看及修改JVM中运行时的数据结构,尽管这些功能在JAVA开发本身是不适用的,Unsafe是一个用于研究学习HotSpot虚拟机非常棒的工具,因为它不需要调用C++代码,或者需要创建即时分析的工具。
参考:http://www.2cto.com/kf/201312/262451.html
http://blog.csdn.net/cun_chen/article/details/50397071