不安全的代码: 教你“随心所欲”地在内存中操作Java的类和对象(1)
2017-09-01 01:04
363 查看
原文地址:https://zeroturnaround.com/rebellabs/dangerous-code-how-to-be-unsafe-with-java-classes-objects-in-memory/
一个类在内存里占多少空间?
自己写的对象又要占用多大内存?
对象里的属性又是如何对齐的?
如果你思考过这些问题的话,那么你算是来对地方了。对于我们这些在RebelLabs的Java极客们来说,这些神奇的问题一直在诱惑着我们:如果你有兴趣知道更多类的实现的相关知识,知道更多类的布局结构(layout)会让我们从内存里取出特定域的值变得更加容易,甚至我们可以在内存里为所欲为(Hecker的必备技能哦。。。)。这也就是说,我们能不仅更改内存里的数据,也可以在内存里更改代码。
另外,如果你知道了这些知识,那么你就具备了理解“Off-heap存储”和“高性能序列化”技术实现的能力。这两个技术是基于内存对象结构操作的典型应用呀! 这篇文章涵盖了获取类和对象的内存地址的方法,获取类和对象在内存里的布局结构,和布局结构中各个域的意义。我们希望能尽可能简单明了地讲明,但是说实话,本文不适合Java初学者。读者需要具备一些Java编程的经验才能读懂。
幸运地是,在Unsafe里有一个theUnsafe的域可以被用来获取Unsafe对象。我们可以通过反射机制写一个帮助方法帮助实现我们的愿望。代码如下:(http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/)
我们可以使用sun.misc.Unsafe的allocateIntance方法在宿主VM内为未初始化的对象分配内存空间。之后我们可以正常调用这个对象的构造函数。
我们可以通过实际的内存地址来监测数据。我们用Unsafe是有可能直接查找和修改对象各个域里的值。
我们还可以使用allocateMemory方法在off-heap中分配内存。比如,当allocateDirect 方法被 调用时DirectByteBuffer的构造函数就调用allocateMemory方法。
arrayBaseOffset 和arrayIndexScale这两个方法可以用来实现arraylet。Arraylet是一种能将大的数组分解成许多小对象的技术,以减少实时对大对象的检查、更新和删除的开销。
下一篇中,我们要用Unsafe来获取类的内存地址了,敬请期待!!!
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的类和对象(4)
- 不安全的代码: 教你“随心所欲”地在内存中操作Java的类和对象(2)
- 不安全的代码: 教你“随心所欲”地在内存中操作Java的类和对象(5)
- 危险代码:内存中的Java类和对象为何变得不安全—Part2
- 危险代码:内存中的Java类和对象为何变得不安全—Part1
- 危险代码:内存中的Java类和对象为何变得不安全—Part3
- 危险代码:内存中的Java类和对象为何变得不安全—Part1
- Java代码中new对象的过程在jvm内存中的操作
- 危险代码:内存中的Java类和对象为何变得不安全—Part2
- 危险代码:内存中的Java类和对象为何变得不安全—Part1
- 危险代码:内存中的Java类和对象为何变得不安全—Part4
- 危险代码:如何使用Unsafe操作内存中的Java类和对象
- String对象的声明操作和Java内存管理机制
- 危险代码:如何使用Unsafe操作内存中的Java类和对象—Part4
- 理解User test=new User();Java代码中的等式,都是对类的对象的操作,即等式右侧都是对象而不是类
- JNI中如何在本地代码中生成和操作Java的对象和字符串,并编写应用实例
- 自动生成数据对象代码和CRUD操作的C#代码生成器
- java io操作代码
- 一段用java反射动态构建对象的代码