原文地址:https://zeroturnaround.com/rebellabs/dangerous-code-how-to-be-unsafe-with-java-classes-objects-in-memory/
你是否对Java内存管理机制感到好奇?有没有问过下面这些奇怪的问题呀:
一个类在内存里占多少空间?自己写的对象又要占用多大内存?对象里的属性又是如何对齐的?如果你思考过这些问题的话,那么你算是来对地方了。对于我们这些在RebelLabs的Java极客们来说,这些神奇的问题一直在诱惑着我们:如果你有兴趣知道更多类的实现的相关知识,知道更多类的布局结构(layout)会让我们从内存里取出特定域的值变得更加容易,甚至我们可以在内存里为所欲为(Hecker的必备技能哦。。。)。这也就是说,我们能不仅更改内存里的数据,也可以在内存里更改代码。
另外,如果你知道了这些知识,那么你就具备了理解“Off-heap存储”和“高性能序列化”技术实现的能力。这两个技术是基于内存对象结构操作的典型应用呀! 这篇文章涵盖了获取类和对象的内存地址的方法,获取类和对象在内存里的布局结构,和布局结构中各个域的意义。我们希望能尽可能简单明了地讲明,但是说实话,本文不适合Java初学者。读者需要具备一些Java编程的经验才能读懂。
本文介绍的类和对象是Java SE7的实现,千万不要认为之前和以后的Java版本也是这么实现的呀!! 我们已经把例子代码放到了GitHub上,可以方便你下载参考。代码在这里。
Java最初被设计为一种安全、受控的环境。然而(重点来啦),Java HotSpot VM在实现的时候留了一个”后门“。这个后门提供了很多可以直接对内存和线程操作的底层接口。这个后门就是——sum.misc.Unsafe。这个类可是被广泛地被JDK本身实现过程应用,比如java.nio和java.util.concurrent的实现。可是,这个后门不建议在生产环境中使用,因为这个API非常不安全、不轻便也不稳定。Unsafe这个类提供了一种窥探HotSpot JVM内部实现的方法,并且可以用一些取巧地方法搞些事情。有时它可以被用来研究VM内部机制,并且不用C++调试哦。有时它可以用于分析和开发工具。
sun.misc.Unsafe这个类真的很不安全,所以JDK的开发人员增加了一些检测机制来限制人们使用它。它的构造函数是私有的,并且它的工厂方法getUnsafe()应该被Bootloader加载。从下面代码段的第8行可知,Unsafe类甚至都不能任何非空的类加载器加载。它会抛出SecurityException异常来阻止访问者。
public final class Unsafe { ... private Unsafe() {} private static final Unsafe theUnsafe = new Unsafe(); ... public static Unsafe getUnsafe() { Class cc = sun.reflect.Reflection.getCallerClass(2); if (cc.getClassLoader() != null) throw new SecurityException("Unsafe"); return theUnsafe; } ... }幸运地是,在Unsafe里有一个theUnsafe的域可以被用来获取Unsafe对象。我们可以通过反射机制写一个帮助方法帮助实现我们的愿望。代码如下:(http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/)
public static Unsafe getUnsafe() { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe)f.get(null); } catch (Exception e) { /* ... */ } }下一篇中,我们要用Unsafe来获取类的内存地址了,敬请期待!!!