Unsafe类源码解析

前言

Unsafe,顾名思义,一个不安全的类,那么jdk的开发者为什么要设计一个不安全的类呢?这个类为什么会不安全呢?现在就让我们来揭开Unsafe类的神秘面纱。

1.概述

作为java开发者的我们都知道,java是没有指针的,默认是由JVM进行内存的分配与垃圾回收,那就意味着java不能直接操作内存了?其实不是的,Unsafe类通过JNI的方式访问本地的C++实现库从而使java具有了直接操作内存空间的能力,但这同时也带来了一定的问题,如果不合理地使用Unsafe类操作内存空间,可能导致内存泄漏,是很危险的,这就要看开发者对Unsafe类的熟悉程度了,不是非常熟悉这个类,不推荐使用。而且java官方也是不推荐开发者使用的,官方关于Unsafa的文档很少,并且这个类在jdk8中也不开源,在Openjdk中也是不开源的,只能通过IDE反编译来看它的源码,它的源码中好多的方法都标注了@Deprecated注解,而且也没有注释(也许是反编译过来看不到注释)。(之前传说jdk9要这个类删除,现在jdk12都出来了,这个类不但没有删除,还在jdk.internal.misc包下增加了一个Unsafe类。并且自从jdk9开始,Unsafe类也开源了,与sun.misc.Unsafe不同的是,jdk.internal.misc.Unsafe是不开放给开发者的,应该是供jdk的开发和维护人员使用的)。既然官方不推荐使用,那为什么还要设计这个类呢?因为Unsafe提供了硬件级别的原子性操作,JUC包中用到的CAS算法就是Unsafe类提供的,jdk的开发者用这个类实现了JUC包中的一部分核心功能。同时,Unsafe类就像是java给开发者提供的一个后门,让开发者使用Unsafe就可以在任意内存地址读写数据,而不用经过JVM的调度,也使java更加地灵活,具有一定的C和C++访问内存的能力,对于系统调优有一定的帮助,对于优秀的开发者,也可以使用这个类造一些实用的轮子。可见,Unsafe类还是有一定的用处的,不过在使用时需要小心谨慎。

2.获取Unsafe

2.1Unsafe类的构造器是私有的,采用了单例模式,提供了一个静态方法,是不是可以通过这个静态方法获取到Unsafe对象呢?

public class UnsafeTest {

   public static void main(String[] args) {
       Unsafe unsafe = Unsafe.getUnsafe();
       System.out.println(unsafe);
   }
}
执行结果

报错了,Unsafe本身提供了获取实例的静态方法,那为什么我们在使用时会报错呢?

看下源码(jdk11的,jdk8不开源,反编译过来的不太好看):

private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();

@CallerSensitive
public static Unsafe getUnsafe() {
   Class<?> caller = Reflection.getCallerClass();
   if (!VM.isSystemDomainLoader(caller.getClassLoader()))
       throw new SecurityException("Unsafe");
   return theUnsafe;
}

看到源码,是不是上面的异常有眉目了?就是throw new SecurityException("Unsafe");这句抛出的异常,我们看为什么抛出异常:

if (!VM.isSystemDomainLoader(caller.getClassLoader()))
       throw new SecurityException("Unsafe");

抛出异常就说明不满足if条件,isSystemDomainLoader(caller.getClassLoader())方法就是检查调用者的类加载器是否是启动类加载器,抛出异常就是因为我们自己创建的类不是由启动类加载器加载,而是默认由系统类加载器加载的,故抛出异常。

2.2使用反射获取Unsafe实例

public class UnsafeTest {

   public static void main(String[] args) {
       
       final Field theUnsafe;
       try {
           theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
           theUnsafe.setAccessible(true);
           Unsafe unsafe  = (Unsafe) theUnsafe.get(null);
           System.out.println(unsafe.getClass().getClassLoader());
           System.out.println(unsafe);
       } catch (NoSuchFieldException | IllegalAccessException e) {
           e.printStackTrace();
       }
   }
}
执行结果

发现使用反射获取Unsafe实例是可以获取到的,打印的类加载器的信息为null,说明是由启动类加载加载的。

2.3jdk11的jdk.internal.misc包中的Unsafe类的getUnsafe静态方法中没有要求调用者必须要由启动类加载器加载,源码如下:

private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();

public static Unsafe getUnsafe() {
   return theUnsafe;
}

这回是不是可以直接获取了呢?试一下:

public class UnsafeTest {

   public static void main(String[] args) {
       jdk.internal.misc.Unsafe unsafe = Unsafe.getUnsafe();
       System.out.println(unsafe);
   }
}

又报错了,异常信息如下:

Exception in thread "main" java.lang.IllegalAccessError: class hello.UnsafeTest (in unnamed module @0x6e8dacdf) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @0x6e8dacdf
   at hello.UnsafeTest.main(UnsafeTest.java:9)

意思就是这个类没有开放给我们,我们不能访问。

看一下java.base下的module-info.java文件,可以看到这个Unsafe类确实没有开放给我们,只是开放给以下的jdk的部分模块:

exports jdk.internal.misc to
   java.desktop,
   java.logging,
   java.management,
   java.naming,
   java.net.http,
   java.rmi,
   java.security.jgss,
   java.sql,
   java.xml,
   jdk.attach,
   jdk.charsets,
   jdk.compiler,
   jdk.internal.vm.ci,
   jdk.jfr,
   jdk.jlink,
   jdk.jshell,
   jdk.net,
   jdk.scripting.nashorn,
   jdk.scripting.nashorn.shell,
   jdk.unsupported;

看来直接获取是获取不到的,除非修改openjdk11源码中的module-info.java文件,将jdk.internal.misc这个包开放出来,重新编译。不过作为普通开发者是没有必要修改源码的,通过反射获取就可以了。

jdk11开放给开发者的也是sun.misc包中的Unsafe类,这个包在jdk.unsupported模块下。也是需要使用反射获取实例的。

注:以下涉及到的源码都为java11的源码。

3.重要API分析

Student类:

public class Student {
    private String name;
    private int age;
    public static String info;

   static {
       info = "这是一个优秀的学生";
   }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }
}

开放给我们能用的位于sun.misc包下的Unsafe类的实现其实是调用了更加底层的位于jdk.internal.misc包下的Unsafe类,native方法就是位于这个类中,这点与java8的Unsafe类不同,java8只有一个Unsafe类,这个类中大多都为native方法:

sun.misc.Unsafe源码:

private static final Unsafe theUnsafe = new Unsafe();
private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

3.1获取对象字段或静态属性的内存地址偏移量

源码如下:

//获取对象属性的便宜量
@ForceInline
public long objectFieldOffset(Field f) {
    return theInternalUnsafe.objectFieldOffset(f);
}
//获取静态属性的偏移量
@ForceInline
public long staticFieldOffset(Field f) {
    return theInternalUnsafe.staticFieldOffset(f);
}

举个栗子:

@Test
public void testGetFieldOffset() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    //获取字段偏移量
    long address = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));
    System.out.println(address);

    //获取静态字段偏移量
    long staticAddress = unsafe.staticFieldOffset(Student.class.getDeclaredField("info"));
    System.out.println(staticAddress);
}

3.2内存操作,包括内存的分配,释放,复制

3.2.1分配内存:

//参数为需要分配的内存的字节数
public long allocateMemory(long bytes) {
    return theInternalUnsafe.allocateMemory(bytes);
}

3.2.2重新分配内存(内存扩展):

//第一个参数为已经分配的内存,第二个参数为要扩展的字节数
public long reallocateMemory(long address, long bytes) {
   return theInternalUnsafe.reallocateMemory(address, bytes);
}

3.2.3内存释放:

public void freeMemory(long address) {
    theInternalUnsafe.freeMemory(address);
}

举个栗子:

@Test
public void testMemoryOperate() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    //分配内存
    long address = unsafe.allocateMemory(4);
    System.out.println(address);
    //扩展内存
    long newAddress = unsafe.reallocateMemory(address, 8);
    System.out.println(newAddress);
    //释放内存
    unsafe.freeMemory(newAddress);
}

关于内存操作还有几个方法,这里列出,不做举例,这几个方法笔者平时不常用,源码中也不常用。

3.2.4设置内存:将给定的内存设置为固定值

public void setMemory(Object o, long offset, long bytes, byte value) {
    theInternalUnsafe.setMemory(o, offset, bytes, value);
}

public void setMemory(long address, long bytes, byte value) {
    theInternalUnsafe.setMemory(address, bytes, value);
}

3.2.5内存复制:

public void copyMemory(Object srcBase, long srcOffset,
                       Object destBase, long destOffset,
                       long bytes) {
    theInternalUnsafe.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
}

public void copyMemory(long srcAddress, long destAddress, long bytes) {
    theInternalUnsafe.copyMemory(srcAddress, destAddress, bytes);
}

3.3内存屏障
为了保证内存的可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令类禁止特定类型的处理器重排序,java内存模型(JMM)把内存屏障指令分为4类:

  • LoadLoad(Load1,LoadLoad,Load2):确保load1数据的装载先于load2及所有后序装载指令的装载。
  • LoadStore(Load1,LoadStore,Store2):确保Load1数据的装载先于Store2及所有后序存储指令刷新内存。
  • StoreStore(Store1,StoreStore,Store2):确保Store1数据刷新内存先于Store2及所有后序存储指令刷新内存。
  • StoreLoad(Store1,StoreLoad,Load2):确保Store1数据刷新内存先于Load2及所有后序装载指令的装载。该屏蔽指令会使该屏蔽之前的所有内存访问指令执行完成后才执行屏蔽之后的内存访问指令。并且这个指令是一个全能的指令,同时具备以上三个内存屏蔽指令的功能。
//确保在屏蔽之前加载(读操作),屏蔽之后的读写操作不会重排序,相当于LoadLoad 加上 LoadStore 屏障
public void loadFence() {
   theInternalUnsafe.loadFence();
}
//确保在屏蔽之前加载和存储(读写操作),屏蔽之后的写操作不会重排序,相当于StoreStore 加上 LoadStore 屏障
public void storeFence() {
   theInternalUnsafe.storeFence();
}
//确保在屏蔽之前读写操作,屏蔽之后的读写操作不会重排序,同时具有以上两个方法的功能,还具有StoreLoad屏蔽的功能
public void fullFence() {
   theInternalUnsafe.fullFence();
}

3.4通过操作内存从一个给定的java变量获取或设置值

这类方法有针对于基本数据类型和对象类型(下面列出比分源码):

//从基本数据类型变量获取值
public int getInt(Object o, long offset) {
    return theInternalUnsafe.getInt(o, offset);
}
//设置基本类型变量的值
public void putInt(Object o, long offset, int x) {
    theInternalUnsafe.putInt(o, offset, x);
}
//从对象类型变量获取值
public Object getObject(Object o, long offset) {
    return theInternalUnsafe.getObject(o, offset);
}
//设置对象类型变量的值
public void putObject(Object o, long offset, Object x) {
    theInternalUnsafe.putObject(o, offset, x);
}

栗子(以int类型为例):

@Test
public void testGetAndPutInt() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    Student student = new Student("liming",16);
    //通过objectFieldOffset方法获取学生年龄在内存中的偏移量
    long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));
    System.out.println(offset);
    //通过操作内存的方式获取学生年龄
    int anInt = unsafe.getInt(student, offset);
    System.out.println(anInt);
    System.out.println(student.getAge());
    //通过操作内存的方式修改学生年龄
    unsafe.putInt(student,offset,30);
    System.out.println(student.getAge());
}

栗子(以Object类型为例):

@Test
public void testGetAndPutObject() throws NoSuchFieldException, IllegalAccessException {
   final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
   theUnsafe.setAccessible(true);
   Unsafe unsafe = (Unsafe) theUnsafe.get(null);

   Student student = new Student("liming",16);
   //通过objectFieldOffset方法获取学生姓名在内存中的偏移量
   long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));
   //获取对象属性
   System.out.println(unsafe.getObject(student,offset));
   System.out.println(student.getName());
   //修改对象属性
   unsafe.putObject(student,offset,"xiahua");
   System.out.println(student.getName());
}

3.5通过操作内存从一个内存地址获取或设置值

这类方法和3.4一样,有基本数据类型和对象类型,这里以int类型为例举个栗子:

//从一个内存地址获取整型值
public int getInt(long address) {
    return theInternalUnsafe.getInt(address);
}
//给一个内存地址设置一个整型值
public void putInt(long address, int x) {
    theInternalUnsafe.putInt(address, x);
}

栗子:

@Test
public void testGetAndPutInt2() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    //通过allocateMemory方法分配一个8具有8个字节的内存空间
    long address = unsafe.allocateMemory(8);

    //给指定的内存空间写入一个整型值
    unsafe.putInt(address,60);
    //获取内存空间的值
    System.out.println(unsafe.getInt(address));

    //重新分配内存,扩大到16字节
    final long newAddress = unsafe.reallocateMemory(address, 16);
    unsafe.putInt(newAddress,100);
    System.out.println(unsafe.getInt(newAddress));

    //回收分配的内存空间
    unsafe.freeMemory(newAddress);
}

3.6通过操作内存从一个给定的变量获取或设置一个具有volatile语义的值

这类方法也和3.4一样,有基本数据类型和对象类型,其实就是volatile版的3.4方法,这里以int类型为例:

//从一个变量获取具有volatile语义的整型值
public int getIntVolatile(Object o, long offset) {
    return theInternalUnsafe.getIntVolatile(o, offset);
}
//给一个变量设置具有volatile语义的值
public void putIntVolatile(Object o, long offset, int x) {
    theInternalUnsafe.putIntVolatile(o, offset, x);
}

栗子:

@Test
public void testGetAndPutIntVolatile() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));

    //设置学生的年龄,使用volatile语义,设置后立马更新到内存对其他线程可见。
    unsafe.putIntVolatile(Student.class,offset,32);
    System.out.println(unsafe.getIntVolatile(Student.class,offset));
}

3.7通过操作内存从一个给定的变量惰性地设置一个具有volatile语义的值

这类方有三个,有基本数据类型和对象类型,其实就是lazy加volatile版的3.4方法,这里以int类型为例:

public void putOrderedObject(Object o, long offset, Object x) {
    theInternalUnsafe.putObjectRelease(o, offset, x);
}
public void putOrderedInt(Object o, long offset, int x) {
    theInternalUnsafe.putIntRelease(o, offset, x);
}
public void putOrderedLong(Object o, long offset, long x) {
    theInternalUnsafe.putLongRelease(o, offset, x);
}

栗子:

/*
*PutIntVolatile的lazy版,惰性设值,设值后内存不能保证立即对其他线程可见,并且变量和后序内存可以重排序
*/
    @Test
    public void testPutIntVolatileLazy() throws NoSuchFieldException, IllegalAccessException {
        final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);

        long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));

        unsafe.putOrderedInt(Student.class,offset,30);
        System.out.println(unsafe.getIntVolatile(Student.class,offset));
    }

3.8通过操作内存获取或设置数组元素值

//获取数组中第一个元素在内存的偏移地址
public int arrayBaseOffset(Class<?> arrayClass) {
    return theInternalUnsafe.arrayBaseOffset(arrayClass);
}
//获取数组增量元素在内存中的地址
public int arrayIndexScale(Class<?> arrayClass) {
    return theInternalUnsafe.arrayIndexScale(arrayClass);
}

栗子:

@Test
public void testArrayOperate() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    int[] array = new int[]{1,5,8,9,6,4,7};

    //获取数组中第一个元素在内存的偏移地址
    int offset = unsafe.arrayBaseOffset(array.getClass());
    //获取数组增量元素在内存中的地址
    int indexScale = unsafe.arrayIndexScale(array.getClass());

    //通过操作内存地址获取数组的第一个元素
    System.out.println(unsafe.getInt(array,offset));
    //通过操作内存修改数组的第一个元素
    unsafe.putInt(array,offset,3);
    System.out.println(unsafe.getInt(array,offset));
    //通过操作内存获取数组的第三个元素,arrayBaseOffset和arrayIndexScale共同使用,就可以获得任意一个数组元素在内存的地址
    System.out.println(unsafe.getInt(array,indexScale*2+offset));

    //通过循环遍历数组元素
    for (int i = 0; i < array.length; i++) {
        System.out.print(unsafe.getInt(array,i*indexScale+offset) + " ");
    }
}

3.9从一个给定的内存地址获取或设置一个本地指针

//从给定的内存地址获取一个本地指针
public long getAddress(long address) {
    return theInternalUnsafe.getAddress(address);
}
//从给定的内存地址设置一个本地指针
public void putAddress(long address, long x) {
    theInternalUnsafe.putAddress(address, x);
}

栗子:

@Test
public void testGetAndPutAddress() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    //通过allocateMemory方法分配一个8具有8个字节的内存空间
    long address = unsafe.allocateMemory(8);

    //给这个内存中存放一个本地指针
    unsafe.putAddress(address,200);
    //获取指定内存地址的指针
    System.out.println(unsafe.getAddress(address));
    //打印通过putAddress存放的本地指针的大小
    System.out.println(unsafe.addressSize());
    //打印内存页面的大小,通常为4k,即4096   //关于内存页面不了解的可以看下操作系统相关的知识
    System.out.println(unsafe.pageSize());
    //释放内存
    unsafe.freeMemory(address);
}

3.10硬件级别的CAS操作

关于CAS算法不了解的小伙伴可以参考我的另一篇文章:CAS算法

//CAS操作对象类型
public final boolean compareAndSwapObject(Object o //对象, long offset //内存偏移量,
                                          Object expected, //预期值
                                          Object x) { //新值
    return theInternalUnsafe.compareAndSetObject(o, offset, expected, x);
}
//CAS操作int类型
public final boolean compareAndSwapInt(Object o, long offset,
                                       int expected,
                                       int x) {
    return theInternalUnsafe.compareAndSetInt(o, offset, expected, x);
}
//CAS操作long型
public final boolean compareAndSwapLong(Object o, long offset,
                                        long expected,
                                        long x) {
    return theInternalUnsafe.compareAndSetLong(o, offset, expected, x);
}

关于CAS操作,Unsafe源码只给出以上三个方法,其他类型的CAS操作可以转化为以上三种类型,比如boolean类型的CAS操作可以转化为int型的CAS操作。

栗子:

@Test
public void testCAS() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));

    unsafe.putInt(Student.class,offset,25);
    System.out.println(unsafe.getInt(Student.class,offset));
    //通过Unsafe提供的硬件级别的原子操作 CAS算法更新学生的年龄
    unsafe.compareAndSwapInt(Student.class,offset,24,35);
    System.out.println(unsafe.getInt(Student.class,offset));

    unsafe.compareAndSwapInt(Student.class,offset,25,35);
    System.out.println(unsafe.getInt(Student.class,offset));
}

3.11线程的挂起与恢复

Unsafe类提供了线程的挂起与恢复方法,JUC中的锁会用到Unsafe提供的线程挂起与恢复方法,源码如下:

//线程挂起,直到unpark调用或者打断或者超时,线程恢复。
//isAbsolute设置是否为绝对时间,如果为true,则超时时间的单位为毫秒
//isAbsolute为false,time设置为0,就是一直阻塞,如果time不为0,则超时时间的单位为纳秒
public void park(boolean isAbsolute, long time) {
   theInternalUnsafe.park(isAbsolute, time);
}
//线程恢复
public void unpark(Object thread) {
   theInternalUnsafe.unpark(thread);
}

栗子:

@Test
public void testParkAndUnPark() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    //写一个线程执行一个任务
    Thread thread = new Thread(() -> System.out.println("hello"));
    thread.start();
    unsafe.unpark(Thread.currentThread());  //如果不加这句,线程或一直处于阻塞状态
    unsafe.park(false,0);
}

3.12从一个变量获取值并让它自动增加给定的值

public final int getAndAddInt(Object o, long offset, int delta) {
   return theInternalUnsafe.getAndAddInt(o, offset, delta);
}

public final long getAndAddLong(Object o, long offset, long delta) {
   return theInternalUnsafe.getAndAddLong(o, offset, delta);
}

举个栗子:

@Test
public void testGetAndAddInt() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));

    unsafe.putInt(Student.class,offset,30);
    System.out.println(unsafe.getAndAddInt(Student.class,offset,3));
    System.out.println(unsafe.getInt(Student.class,offset));
}

3.13从一个变量获取值并设置成另一个值

public final int getAndSetInt(Object o, long offset, int newValue) {
    return theInternalUnsafe.getAndSetInt(o, offset, newValue);
}

public final long getAndSetLong(Object o, long offset, long newValue) {
    return theInternalUnsafe.getAndSetLong(o, offset, newValue);
}

public final Object getAndSetObject(Object o, long offset, Object newValue) {
    return theInternalUnsafe.getAndSetObject(o, offset, newValue);
}

栗子:

public void testGetAndSetInt() throws NoSuchFieldException, IllegalAccessException {
    final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
    theUnsafe.setAccessible(true);
    Unsafe unsafe = (Unsafe) theUnsafe.get(null);

    long offset = unsafe.objectFieldOffset(Student.class.getDeclaredField("age"));

    unsafe.putInt(Student.class,offset,30);
    System.out.println(unsafe.getAndSetInt(Student.class,offset,32));
    System.out.println(unsafe.getInt(Student.class,offset));
}

4.Unsafe底层实现

既然Unsafe能够操作内存,那么它的底层一定需要JVM的支持,在OpenJDK的HotSpot虚拟机源码中有Unsafe类的JVM层面的实现,对应于prims目录中的Unsafe.cpp,java11的jvm源码关于Unsafe的实现还多了Unsafe.hpp这个头文件。Unsafe.cpp这个文件小两千行代码,且注释比较少,要想全搞明白需要Debug很久,这里我就不具体分析了,以后写JVM源码时再具体分析这个类。如果感兴趣的朋友可以下载OpenJDK源码,编译后搭建调试环境,关于JVM源码编译,大家可以参考我的这篇文章:Ubuntu下编译openjdk8

Ubuntu下编译openjdk11

调试环境的搭建,大家可以参考我的这篇文章:JVM源码调试环境搭建

OpenJDK大家可以到官网下载,也可以到我的github下载:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容