Unsafe類源碼解析

前言

Unsafe,顧名思義,一個不安全的類,那么jdk的開發(fā)者為什么要設計一個不安全的類呢?這個類為什么會不安全呢?現在就讓我們來揭開Unsafe類的神秘面紗。

1.概述

作為java開發(fā)者的我們都知道,java是沒有指針的,默認是由JVM進行內存的分配與垃圾回收,那就意味著java不能直接操作內存了?其實不是的,Unsafe類通過JNI的方式訪問本地的C++實現庫從而使java具有了直接操作內存空間的能力,但這同時也帶來了一定的問題,如果不合理地使用Unsafe類操作內存空間,可能導致內存泄漏,是很危險的,這就要看開發(fā)者對Unsafe類的熟悉程度了,不是非常熟悉這個類,不推薦使用。而且java官方也是不推薦開發(fā)者使用的,官方關于Unsafa的文檔很少,并且這個類在jdk8中也不開源,在Openjdk中也是不開源的,只能通過IDE反編譯來看它的源碼,它的源碼中好多的方法都標注了@Deprecated注解,而且也沒有注釋(也許是反編譯過來看不到注釋)。(之前傳說jdk9要這個類刪除,現在jdk12都出來了,這個類不但沒有刪除,還在jdk.internal.misc包下增加了一個Unsafe類。并且自從jdk9開始,Unsafe類也開源了,與sun.misc.Unsafe不同的是,jdk.internal.misc.Unsafe是不開放給開發(fā)者的,應該是供jdk的開發(fā)和維護人員使用的)。既然官方不推薦使用,那為什么還要設計這個類呢?因為Unsafe提供了硬件級別的原子性操作,JUC包中用到的CAS算法就是Unsafe類提供的,jdk的開發(fā)者用這個類實現了JUC包中的一部分核心功能。同時,Unsafe類就像是java給開發(fā)者提供的一個后門,讓開發(fā)者使用Unsafe就可以在任意內存地址讀寫數據,而不用經過JVM的調度,也使java更加地靈活,具有一定的C和C++訪問內存的能力,對于系統(tǒng)調優(yōu)有一定的幫助,對于優(yōu)秀的開發(fā)者,也可以使用這個類造一些實用的輪子??梢姡琔nsafe類還是有一定的用處的,不過在使用時需要小心謹慎。

2.獲取Unsafe

2.1Unsafe類的構造器是私有的,采用了單例模式,提供了一個靜態(tài)方法,是不是可以通過這個靜態(tài)方法獲取到Unsafe對象呢?

public class UnsafeTest {

   public static void main(String[] args) {
       Unsafe unsafe = Unsafe.getUnsafe();
       System.out.println(unsafe);
   }
}
執(zhí)行結果

報錯了,Unsafe本身提供了獲取實例的靜態(tài)方法,那為什么我們在使用時會報錯呢?

看下源碼(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())方法就是檢查調用者的類加載器是否是啟動類加載器,拋出異常就是因為我們自己創(chuàng)建的類不是由啟動類加載器加載,而是默認由系統(tǒng)類加載器加載的,故拋出異常。

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();
       }
   }
}
執(zhí)行結果

發(fā)現使用反射獲取Unsafe實例是可以獲取到的,打印的類加載器的信息為null,說明是由啟動類加載加載的。

2.3jdk11的jdk.internal.misc包中的Unsafe類的getUnsafe靜態(tài)方法中沒有要求調用者必須要由啟動類加載器加載,源碼如下:

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這個包開放出來,重新編譯。不過作為普通開發(fā)者是沒有必要修改源碼的,通過反射獲取就可以了。

jdk11開放給開發(fā)者的也是sun.misc包中的Unsafe類,這個包在jdk.unsupported模塊下。也是需要使用反射獲取實例的。

注:以下涉及到的源碼都為java11的源碼。

3.重要API分析

Student類:

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

   static {
       info = "這是一個優(yōu)秀的學生";
   }

    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獲取對象字段或靜態(tài)屬性的內存地址偏移量

源碼如下:

//獲取對象屬性的便宜量
@ForceInline
public long objectFieldOffset(Field f) {
    return theInternalUnsafe.objectFieldOffset(f);
}
//獲取靜態(tài)屬性的偏移量
@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);

    //獲取靜態(tài)字段偏移量
    long staticAddress = unsafe.staticFieldOffset(Student.class.getDeclaredField("info"));
    System.out.println(staticAddress);
}

3.2內存操作,包括內存的分配,釋放,復制

3.2.1分配內存:

//參數為需要分配的內存的字節(jié)數
public long allocateMemory(long bytes) {
    return theInternalUnsafe.allocateMemory(bytes);
}

3.2.2重新分配內存(內存擴展):

//第一個參數為已經分配的內存,第二個參數為要擴展的字節(jié)數
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及所有后序裝載指令的裝載。該屏蔽指令會使該屏蔽之前的所有內存訪問指令執(zhí)行完成后才執(zhí)行屏蔽之后的內存訪問指令。并且這個指令是一個全能的指令,同時具備以上三個內存屏蔽指令的功能。
//確保在屏蔽之前加載(讀操作),屏蔽之后的讀寫操作不會重排序,相當于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個字節(jié)的內存空間
    long address = unsafe.allocateMemory(8);

    //給指定的內存空間寫入一個整型值
    unsafe.putInt(address,60);
    //獲取內存空間的值
    System.out.println(unsafe.getInt(address));

    //重新分配內存,擴大到16字節(jié)
    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));

    //通過循環(huán)遍歷數組元素
    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個字節(jié)的內存空間
    long address = unsafe.allocateMemory(8);

    //給這個內存中存放一個本地指針
    unsafe.putAddress(address,200);
    //獲取指定內存地址的指針
    System.out.println(unsafe.getAddress(address));
    //打印通過putAddress存放的本地指針的大小
    System.out.println(unsafe.addressSize());
    //打印內存頁面的大小,通常為4k,即4096   //關于內存頁面不了解的可以看下操作系統(tǒng)相關的知識
    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);

    //寫一個線程執(zhí)行一個任務
    Thread thread = new Thread(() -> System.out.println("hello"));
    thread.start();
    unsafe.unpark(Thread.currentThread());  //如果不加這句,線程或一直處于阻塞狀態(tài)
    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源碼,編譯后搭建調試環(huán)境,關于JVM源碼編譯,大家可以參考我的這篇文章:Ubuntu下編譯openjdk8

Ubuntu下編譯openjdk11

調試環(huán)境的搭建,大家可以參考我的這篇文章:JVM源碼調試環(huán)境搭建

OpenJDK大家可以到官網下載,也可以到我的github下載:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容