您的位置:首页 > 编程语言 > Java开发

不安全的代码: 教你“随心所欲”地在内存中操作Java的类和对象(1)

2017-09-01 01:04 363 查看
原文地址:https://zeroturnaround.com/rebellabs/dangerous-code-how-to-be-unsafe-with-java-classes-objects-in-memory/



Java的类和对象在内存里到底是什么样子的呀?我们一起来搞搞清楚撒。。。

你是否对Java内存管理机制感到好奇?有没有问过下面这些奇怪的问题呀:

一个类在内存里占多少空间?

自己写的对象又要占用多大内存?

对象里的属性又是如何对齐的?

如果你思考过这些问题的话,那么你算是来对地方了。对于我们这些在RebelLabs的Java极客们来说,这些神奇的问题一直在诱惑着我们:如果你有兴趣知道更多类的实现的相关知识,知道更多类的布局结构(layout)会让我们从内存里取出特定域的值变得更加容易,甚至我们可以在内存里为所欲为(Hecker的必备技能哦。。。)。这也就是说,我们能不仅更改内存里的数据,也可以在内存里更改代码

另外,如果你知道了这些知识,那么你就具备了理解“Off-heap存储”和“高性能序列化”技术实现的能力。这两个技术是基于内存对象结构操作的典型应用呀! 这篇文章涵盖了获取类和对象的内存地址的方法,获取类和对象在内存里的布局结构,和布局结构中各个域的意义。我们希望能尽可能简单明了地讲明,但是说实话,本文不适合Java初学者。读者需要具备一些Java编程的经验才能读懂。

注意啊!!

本文介绍的类和对象是Java SE7的实现,千万不要认为之前和以后的Java版本也是这么实现的呀!! 我们已经把例子代码放到了GitHub上,可以方便你下载参考。代码在这里

Java里的直接内存访问的方法

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中一些有用的特性

虚拟机的固有的特性。就像CAS(compare-and-swap)技术被用于无锁哈希表(Lock-Free Hash Table)。比如,compareAndSwapInt方法使得JNI调用支持特定CAS指令的Native代码。想要了解更多CAS的知识,请点这里

我们可以使用sun.misc.Unsafe的allocateIntance方法在宿主VM内为未初始化的对象分配内存空间。之后我们可以正常调用这个对象的构造函数。

我们可以通过实际的内存地址来监测数据。我们用Unsafe是有可能直接查找和修改对象各个域里的值。

我们还可以使用allocateMemory方法在off-heap中分配内存。比如,当allocateDirect 方法被 调用时DirectByteBuffer的构造函数就调用allocateMemory方法。

arrayBaseOffset 和arrayIndexScale这两个方法可以用来实现arraylet。Arraylet是一种能将大的数组分解成许多小对象的技术,以减少实时对大对象的检查、更新和删除的开销。

下一篇中,我们要用Unsafe来获取类的内存地址了,敬请期待!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 安全 对象 内存 class