<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          各大框架都在使用的Unsafe類,到底有多神奇?

          共 5791字,需瀏覽 12分鐘

           ·

          2022-04-28 19:48

          前言

          幾乎每個使用 Java開發(fā)的工具、軟件基礎設施、高性能開發(fā)庫都在底層使用了sun.misc.Unsafe,比如Netty、Cassandra、Hadoop、Kafka等。

          Unsafe類在提升Java運行效率,增強Java語言底層操作能力方面起了很大的作用。但Unsafe類在sun.misc包下,不屬于Java標準。

          很早之前,在閱讀并發(fā)編程相關類的源碼時,看到Unsafe類,產生了一個疑惑:既然是并發(fā)編程中用到的類,為什么命名為Unsafe呢?

          深入了解之后才知道,這里的Unsafe并不是說線程安全與否,而是指:該類對于普通的程序員來說是”危險“的,一般應用開發(fā)者不會也不應該用到此類。

          因為Unsafe類功能過于強大,提供了一些可以繞開JVM的更底層功能。它讓Java擁有了像C語言的指針一樣操作內存空間的能力,能夠提升效率,但也帶來了指針的問題。官方并不建議使用,也沒提供文檔支持,甚至計劃在高版本中去掉該類。

          但對于開發(fā)者來說,了解該類提供的功能更有助于我們學習CAS、并發(fā)編程等相關的知識,還是非常有必要學習和了解的。

          Unsafe的構造

          Unsafe類是"final"的,不允許繼承,且構造函數(shù)是private,使用了單例模式來通過一個靜態(tài)方法getUnsafe()來獲取。

          ????private?Unsafe()?{
          ????}

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

          在getUnsafe方法中對單例模式中的對象創(chuàng)建做了限制,如果是普通的調用會拋出一個SecurityException異常。只有由主類加載器加載的類才能調用這個方法。

          那么,如何獲得Unsafe類的對象呢?通常采用反射機制:

          public?static?Unsafe?getUnsafe()?throws?IllegalAccessException?{
          ??Field?unsafeField?=?Unsafe.class.getDeclaredFields()[0];
          ??unsafeField.setAccessible(true);
          ??return?(Unsafe)?unsafeField.get(null);
          }

          當獲得Unsafe對象之后,就可以”為所欲為“了。下面就來看看,通過Unsafe方法,我們可以做些什么。

          Unsafe的主要功能

          可先從根據(jù)下圖從整體上了解一下Unsafe提供的功能:

          Unsafe功能概述

          下面挑選重要的功能進行講解。

          一、內存管理

          Unsafe的內存管理功能主要包括:普通讀寫、volatile讀寫、有序寫入、直接操作內存等分配內存與釋放內存的功能。

          普通讀寫

          Unsafe可以讀寫一個類的屬性,即便這個屬性是私有的,也可以對這個屬性進行讀寫。

          //?獲取內存地址指向的整數(shù)
          public?native?int?getInt(Object?var1,?long?var2);

          //?將整數(shù)寫入指定內存地址
          public?native?void?putInt(Object?var1,?long?var2,?int?var4);

          getInt用于從對象的指定偏移地址處讀取一個int。putInt用于在對象指定偏移地址處寫入一個int。其他原始類型也提供有對應的方法。

          另外,Unsafe的getByte、putByte方法提供了直接在一個地址上進行讀寫的功能。

          volatile讀寫

          普通的讀寫無法保證可見性和有序性,而volatile讀寫就可以保證可見性和有序性。

          //?獲取內存地址指向的整數(shù),并支持volatile語義
          public?native?int?getIntVolatile(Object?var1,?long?var2);

          //?將整數(shù)寫入指定內存地址,并支持volatile語義
          public?native?void?putIntVolatile(Object?var1,?long?var2,?int?var4);

          volatile讀寫要保證可見性和有序性,相對普通讀寫更加昂貴。

          有序寫入

          有序寫入只保證寫入的有序性,不保證可見性,就是說一個線程的寫入不保證其他線程立馬可見。

          //?將整數(shù)寫入指定內存地址、有序或者有延遲的方法
          public?native?void?putOrderedInt(Object?var1,?long?var2,?int?var4);

          而與volatile寫入相比putOrderedXX寫入代價相對較低,putOrderedXX寫入不保證可見性,但是保證有序性,所謂有序性,就是保證指令不會重排序。

          直接操作內存

          Unsafe提供了直接操作內存的能力:

          //?分配內存
          public?native?long?allocateMemory(long?var1);
          //?重新分配內存
          public?native?long?reallocateMemory(long?var1,?long?var3);
          //?內存初始化
          public?native?void?setMemory(long?var1,?long?var3,?byte?var5);
          //?內存復制
          public?native?void?copyMemory(Object?var1,?long?var2,?Object?var4,?long?var5,?long?var7);
          //?清除內存
          public?native?void?freeMemory(long?var1);

          對應操作內存,也提供了一些獲取內存信息的方法:

          //?獲取內存地址
          public?native?long?getAddress(long?var1);

          public?native?int?addressSize();

          public?native?int?pageSize();

          值得注意的是:利用copyMemory方法可以實現(xiàn)一個通用的對象拷貝方法,無需再對每一個對象都實現(xiàn)clone方法,但只能做到對象淺拷貝。

          二、非常規(guī)對象實例化

          通常,我們通過new或反射來實例化對象,而Unsafe類提供的allocateInstance方法,可以直接生成對象實例,且無需調用構造方法和其他初始化方法。

          這在對象反序列化的時候會很有用,能夠重建和設置final字段,而不需要調用構造方法。

          //?直接生成對象實例,不會調用這個實例的構造方法
          public?native?Object?allocateInstance(Class?var1)?throws?InstantiationException;

          三、類加載

          通過以下方法,可以實現(xiàn)類的定義、創(chuàng)建等操作。

          //?方法定義一個類,用于動態(tài)地創(chuàng)建類
          public?native?Class?defineClass(String?var1,?byte[]?var2,?int?var3,?int?var4,?ClassLoader?var5,?ProtectionDomain?var6);

          //??動態(tài)的創(chuàng)建一個匿名內部類
          public?native?Class?defineAnonymousClass(Class?var1,?byte[]?var2,?Object[]?var3);

          //?判斷是否需要初始化一個類
          public?native?boolean?shouldBeInitialized(Class?var1);

          //?保證已經初始化過一個類
          public?native?void?ensureClassInitialized(Class?var1);

          四、偏移量相關

          Unsafe提供以下方法獲取對象的指針,通過對指針進行偏移,不僅可以直接修改指針指向的數(shù)據(jù)(即使它們是私有的),甚至可以找到JVM已經認定為垃圾、可以進行回收的對象。

          //?獲取靜態(tài)屬性Field在對象中的偏移量,讀寫靜態(tài)屬性時必須獲取其偏移量
          public?native?long?staticFieldOffset(Field?var1);
          //?獲取非靜態(tài)屬性Field在對象實例中的偏移量,讀寫對象的非靜態(tài)屬性時會用到這個偏移量
          public?native?long?objectFieldOffset(Field?var1);
          //?返回Field所在的對象
          public?native?Object?staticFieldBase(Field?var1);
          //?返回數(shù)組中第一個元素實際地址相對整個數(shù)組對象的地址的偏移量
          public?native?int?arrayBaseOffset(Class?var1);
          //?計算數(shù)組中第一個元素所占用的內存空間
          public?native?int?arrayIndexScale(Class?var1);

          五、數(shù)組操作

          數(shù)組操作提供了以下方法:

          //?獲取數(shù)組第一個元素的偏移地址
          public?native?int?arrayBaseOffset(Class?var1);
          //?獲取數(shù)組中元素的增量地址
          public?native?int?arrayIndexScale(Class?var1);

          arrayBaseOffset與arrayIndexScale配合起來使用,就可以定位數(shù)組中每個元素在內存中的位置。

          由于Java的數(shù)組最大值為Integer.MAX_VALUE,使用Unsafe類的內存分配方法可以實現(xiàn)超大數(shù)組。實際上這樣的數(shù)據(jù)就可以認為是C數(shù)組,因此需要注意在合適的時間釋放內存。

          六、線程調度

          線程調度相關方法如下:

          //?喚醒線程
          public?native?void?unpark(Object?var1);
          //?掛起線程
          public?native?void?park(boolean?var1,?long?var2);
          //?用于加鎖,已廢棄
          public?native?void?monitorEnter(Object?var1);
          //?用于加鎖,已廢棄
          public?native?void?monitorExit(Object?var1);
          //?用于加鎖,已廢棄
          public?native?boolean?tryMonitorEnter(Object?var1);

          通過park方法將線程進行掛起, 線程將一直阻塞到超時或中斷條件出現(xiàn)。unpark方法可以終止一個掛起的線程,使其恢復正常。

          整個并發(fā)框架中對線程的掛起操作被封裝在LockSupport類中,LockSupport類中有各種版本pack方法,但最終都調用了Unsafe.park()方法。

          七、CAS操作

          Unsafe類的CAS操作可能是使用最多的方法。它為Java的鎖機制提供了一種新的解決辦法,比如AtomicInteger等類都是通過該方法來實現(xiàn)的。compareAndSwap方法是原子的,可以避免繁重的鎖機制,提高代碼效率。

          public?final?native?boolean?compareAndSwapObject(Object?var1,?long?var2,?Object?var4,?Object?var5);

          public?final?native?boolean?compareAndSwapInt(Object?var1,?long?var2,?int?var4,?int?var5);

          public?final?native?boolean?compareAndSwapLong(Object?var1,?long?var2,?long?var4,?long?var6);

          CAS一般用于樂觀鎖,它在Java中有廣泛的應用,ConcurrentHashMap,ConcurrentLinkedQueue中都有用到CAS來實現(xiàn)樂觀鎖。

          八、內存屏障

          JDK8新引入了用于定義內存屏障、避免代碼重排的方法:

          //?保證在這個屏障之前的所有讀操作都已經完成
          public?native?void?loadFence();

          //?保證在這個屏障之前的所有寫操作都已經完成
          public?native?void?storeFence();

          //?保證在這個屏障之前的所有讀寫操作都已經完成
          public?native?void?fullFence();

          九、其他

          當然,Unsafe類中還提供了大量其他的方法,比如上面提到的CAS操作,以AtomicInteger為例,當我們調用getAndIncrement、getAndDecrement等方法時,本質上調用的就是Unsafe的getAndAddInt方法。

          public?final?int?getAndIncrement()?{
          ????return?unsafe.getAndAddInt(this,?valueOffset,?1);
          }

          public?final?int?getAndDecrement()?{
          ????return?unsafe.getAndAddInt(this,?valueOffset,?-1);
          }

          在實踐的過程中,如果閱讀其他框架或類庫實現(xiàn),當發(fā)現(xiàn)用到Unsafe類,可對照該類的整體功能,結合應用場景進行分析,即可大概了解其功能。

          小結

          經過本文的分析,想必大家在閱讀源碼時,再遇到Unsafe類的調用,一定大概猜出它是用來干什么的。使用Unsafe類的主要目的大多數(shù)情況下是為了提升運行效率、增強功能。但同時也面臨著出錯、內存管理等風險。只有深入了解,且有必要的情況下才建議使用。


          往期推薦

          synchronized底層是如何實現(xiàn)的?


          線程休眠竟然有 5 種方法?


          蝦皮二面:什么是零拷貝?如何實現(xiàn)零拷貝?


          瀏覽 53
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  3w,操逼网站免费播放 | 免费又色又爽又黄的成人用品 | 7777无码 | 欧美日韩一级二级三级 | 乖乖女被黑人巨粗进入 |