0x01 前言
sun.misc.Unsafe是Java为自己而生的底层API
正常情况下开发者是无法直接实例化sun.misc.Unsafe类的,只能通过反射来获取实例
并且该类在Java的大版本中
例如:Java8, Java11 可以使用的方法也是不同的
0x02 sun.misc.Unsafe 代码片段讲解
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package sun.misc;import java.lang.reflect.Field;import java.lang.reflect.Modifier;import java.security.ProtectionDomain;import sun.reflect.CallerSensitive;import sun.reflect.Reflection;public final class Unsafe {private static final Unsafe theUnsafe;省去其它不重要代码......private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(var0.getClassLoader())) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}省去其它不重要代码......static {registerNatives();Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});theUnsafe = new Unsafe();省去其它不重要代码......}}
从上面的片段代码中可以看到Unsafe无法被继承也无法通过new的方式创建Unsafe类实例
如果想通过getUnsafe()方法获取Unsafe实例还会检查类加载器
可以看到虽然getUnsafe()方法是可以被调用,但是会有判断
内部会检查该CallerClass是不是由系统类加载器BootstrapClassLoader加载
也就是说默认情况下只允许由系统类加载器BootstrapClassLoader加载
因此如果想要拿到Unsafe类实例那么最优方案既是反射的方式
0x03 反射获取Unsafe类实例
0x03.1 方法一
package UnsafeTest;import sun.misc.Unsafe;import java.lang.reflect.Field;public class Test {public static void main(String[] args) {// 反射获取 Unsafe 的 theUnsafe 成员变量Field unsafeField = null;try {unsafeField = Unsafe.class.getDeclaredField("theUnsafe");// 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量unsafeField.setAccessible(true);// 通过 unsafeField 得到该 Field 对应的具体对象// 传入 null 是因为该 Field 为 staticUnsafe unsafe = (Unsafe) unsafeField.get(null);} catch (IllegalAccessException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();}}}
0x03.2 方法二
package UnsafeTest;import sun.misc.Unsafe;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class Test2 {public static void main(String[] args) {// 获取Unsafe无参构造方法Constructor constructor = null;try {constructor = Unsafe.class.getDeclaredConstructor();// 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量constructor.setAccessible(true);// 反射创建Unsafe类实例// 等于 Unsafe unsafe2 = new Unsafe();Unsafe unsafe2 = (Unsafe) constructor.newInstance();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}
0x04 总结
通过 0x03 就可以获取到Unsafe对象了,也就获得了调用Unsafe内部方法的权限
