0x01 前言

sun.misc.Unsafe是Java为自己而生的底层API
正常情况下开发者是无法直接实例化sun.misc.Unsafe类的,只能通过反射来获取实例

并且该类在Java的大版本中
例如:Java8, Java11 可以使用的方法也是不同的

0x02 sun.misc.Unsafe 代码片段讲解

  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by Fernflower decompiler)
  4. //
  5. package sun.misc;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.Modifier;
  8. import java.security.ProtectionDomain;
  9. import sun.reflect.CallerSensitive;
  10. import sun.reflect.Reflection;
  11. public final class Unsafe {
  12. private static final Unsafe theUnsafe;
  13. 省去其它不重要代码......
  14. private Unsafe() {
  15. }
  16. @CallerSensitive
  17. public static Unsafe getUnsafe() {
  18. Class var0 = Reflection.getCallerClass();
  19. if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
  20. throw new SecurityException("Unsafe");
  21. } else {
  22. return theUnsafe;
  23. }
  24. }
  25. 省去其它不重要代码......
  26. static {
  27. registerNatives();
  28. Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
  29. theUnsafe = new Unsafe();
  30. 省去其它不重要代码......
  31. }
  32. }

从上面的片段代码中可以看到Unsafe无法被继承也无法通过new的方式创建Unsafe类实例

如果想通过getUnsafe()方法获取Unsafe实例还会检查类加载器
可以看到虽然getUnsafe()方法是可以被调用,但是会有判断
内部会检查该CallerClass是不是由系统类加载器BootstrapClassLoader加载
也就是说默认情况下只允许由系统类加载器BootstrapClassLoader加载

因此如果想要拿到Unsafe类实例那么最优方案既是反射的方式

0x03 反射获取Unsafe类实例

0x03.1 方法一

  1. package UnsafeTest;
  2. import sun.misc.Unsafe;
  3. import java.lang.reflect.Field;
  4. public class Test {
  5. public static void main(String[] args) {
  6. // 反射获取 Unsafe 的 theUnsafe 成员变量
  7. Field unsafeField = null;
  8. try {
  9. unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
  10. // 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量
  11. unsafeField.setAccessible(true);
  12. // 通过 unsafeField 得到该 Field 对应的具体对象
  13. // 传入 null 是因为该 Field 为 static
  14. Unsafe unsafe = (Unsafe) unsafeField.get(null);
  15. } catch (IllegalAccessException e) {
  16. e.printStackTrace();
  17. } catch (NoSuchFieldException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

0x03.2 方法二

  1. package UnsafeTest;
  2. import sun.misc.Unsafe;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.InvocationTargetException;
  5. public class Test2 {
  6. public static void main(String[] args) {
  7. // 获取Unsafe无参构造方法
  8. Constructor constructor = null;
  9. try {
  10. constructor = Unsafe.class.getDeclaredConstructor();
  11. // 忽略访问修饰符的检查,这样就可以调用私有方法或是成员变量
  12. constructor.setAccessible(true);
  13. // 反射创建Unsafe类实例
  14. // 等于 Unsafe unsafe2 = new Unsafe();
  15. Unsafe unsafe2 = (Unsafe) constructor.newInstance();
  16. } catch (NoSuchMethodException e) {
  17. e.printStackTrace();
  18. } catch (InstantiationException e) {
  19. e.printStackTrace();
  20. } catch (IllegalAccessException e) {
  21. e.printStackTrace();
  22. } catch (InvocationTargetException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

0x04 总结

通过 0x03 就可以获取到Unsafe对象了,也就获得了调用Unsafe内部方法的权限