<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發(fā)

      導(dǎo)航

      Java魔法類:sun.misc.Unsafe

      Unsafe類在jdk 源碼的多個類中用到,這個類的提供了一些繞開JVM的更底層功能,基于它的實(shí)現(xiàn)可以提高效率。但是,它是一把雙刃劍:正如它的名字所預(yù)示的那樣,它是Unsafe的,它所分配的內(nèi)存需要手動free(不被GC回收)。Unsafe類,提供了JNI某些功能的簡單替代:確保高效性的同時,使事情變得更簡單。

      這篇文章主要是以下文章的整理、翻譯。

      http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

      1. Unsafe API的大部分方法都是native實(shí)現(xiàn),它由105個方法組成,主要包括以下幾類:

      (1)Info相關(guān)。主要返回某些低級別的內(nèi)存信息:addressSize(), pageSize()

      (2)Objects相關(guān)。主要提供Object和它的域操縱方法:allocateInstance(),objectFieldOffset()

      (3)Class相關(guān)。主要提供Class和它的靜態(tài)域操縱方法:staticFieldOffset(),defineClass(),defineAnonymousClass(),ensureClassInitialized()

      (4)Arrays相關(guān)。數(shù)組操縱方法:arrayBaseOffset(),arrayIndexScale()

      (5)Synchronization相關(guān)。主要提供低級別同步原語(如基于CPU的CAS(Compare-And-Swap)原語):monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt()

      (6)Memory相關(guān)。直接內(nèi)存訪問方法(繞過JVM堆直接操縱本地內(nèi)存):allocateMemory(),copyMemory(),freeMemory(),getAddress(),getInt(),putInt()

      2. Unsafe類實(shí)例的獲取

      Unsafe類設(shè)計(jì)只提供給JVM信任的啟動類加載器所使用,是一個典型的單例模式類。它的實(shí)例獲取方法如下:

      public static Unsafe getUnsafe() {
          Class cc = sun.reflect.Reflection.getCallerClass(2);
          if (cc.getClassLoader() != null)
              throw new SecurityException("Unsafe");
          return theUnsafe;
      }

      非啟動類加載器直接調(diào)用Unsafe.getUnsafe()方法會拋出SecurityException(具體原因涉及JVM類的雙親加載機(jī)制)。

      解決辦法有兩個,其一是通過JVM參數(shù)-Xbootclasspath指定要使用的類為啟動類,另外一個辦法就是java反射了。

      Field f = Unsafe.class.getDeclaredField("theUnsafe");
      f.setAccessible(true);
      Unsafe unsafe = (Unsafe) f.get(null);

      通過將private單例實(shí)例暴力設(shè)置accessible為true,然后通過Field的get方法,直接獲取一個Object強(qiáng)制轉(zhuǎn)換為Unsafe。在IDE中,這些方法會被標(biāo)志為Error,可以通過以下設(shè)置解決:

      Preferences -> Java -> Compiler -> Errors/Warnings ->
      Deprecated and restricted API -> Forbidden reference -> Warning
      3. Unsafe類“有趣”的應(yīng)用場景

      (1)繞過類初始化方法。當(dāng)你想要繞過對象構(gòu)造方法、安全檢查器或者沒有public的構(gòu)造方法時,allocateInstance()方法變得非常有用。

      class A {
          private long a; // not initialized value
       
          public A() {
              this.a = 1; // initialization
          }
       
          public long a() { return this.a; }
      }

      以下是構(gòu)造方法、反射方法和allocateInstance()的對照

      A o1 = new A(); // constructor
      o1.a(); // prints 1
       
      A o2 = A.class.newInstance(); // reflection
      o2.a(); // prints 1
       
      A o3 = (A) unsafe.allocateInstance(A.class); // unsafe
      o3.a(); // prints 0

      allocateInstance()根本沒有進(jìn)入構(gòu)造方法,在單例模式時,我們似乎看到了危機(jī)。

      (2)內(nèi)存修改

      內(nèi)存修改在c語言中是比較常見的,在Java中,可以用它繞過安全檢查器。

      考慮以下簡單準(zhǔn)入檢查規(guī)則:

      class Guard {
          private int ACCESS_ALLOWED = 1;
       
          public boolean giveAccess() {
              return 42 == ACCESS_ALLOWED;
          }
      }

      在正常情況下,giveAccess總會返回false,但事情不總是這樣

      Guard guard = new Guard();
      guard.giveAccess();   // false, no access
       
      // bypass
      Unsafe unsafe = getUnsafe();
      Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
      unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption
       
      guard.giveAccess(); // true, access granted

      通過計(jì)算內(nèi)存偏移,并使用putInt()方法,類的ACCESS_ALLOWED被修改。在已知類結(jié)構(gòu)的時候,數(shù)據(jù)的偏移總是可以計(jì)算出來(與c++中的類中數(shù)據(jù)的偏移計(jì)算是一致的)。

      (3)實(shí)現(xiàn)類似C語言的sizeOf()函數(shù)

      通過結(jié)合Java反射和objectFieldOffset()函數(shù)實(shí)現(xiàn)一個C-like sizeOf()函數(shù)。

      public static long sizeOf(Object o) {
          Unsafe u = getUnsafe();
          HashSet fields = new HashSet();
          Class c = o.getClass();
          while (c != Object.class) {
              for (Field f : c.getDeclaredFields()) {
                  if ((f.getModifiers() & Modifier.STATIC) == 0) {
                      fields.add(f);
                  }
              }
              c = c.getSuperclass();
          }
       
          // get offset
          long maxSize = 0;
          for (Field f : fields) {
              long offset = u.objectFieldOffset(f);
              if (offset > maxSize) {
                  maxSize = offset;
              }
          }
       
          return ((maxSize/8) + 1) * 8;   // padding
      }

      算法的思路非常清晰:從底層子類開始,依次取出它自己和它的所有超類的非靜態(tài)域,放置到一個HashSet中(重復(fù)的只計(jì)算一次,Java是單繼承),然后使用objectFieldOffset()獲得一個最大偏移,最后還考慮了對齊。

      在32位的JVM中,可以通過讀取class文件偏移為12的long來獲取size。

      public static long sizeOf(Object object){
          return getUnsafe().getAddress(
              normalize(getUnsafe().getInt(object, 4L)) + 12L);
      }

      其中normalize()函數(shù)是一個將有符號int轉(zhuǎn)為無符號long的方法

      private static long normalize(int value) {
          if(value >= 0) return value;
          return (0L >>> 32) & value;
      }

      兩個sizeOf()計(jì)算的類的尺寸是一致的。最標(biāo)準(zhǔn)的sizeOf()實(shí)現(xiàn)是使用java.lang.instrument,但是,它需要指定命令行參數(shù)-javaagent。

      (4)實(shí)現(xiàn)Java淺復(fù)制

      標(biāo)準(zhǔn)的淺復(fù)制方案是實(shí)現(xiàn)Cloneable接口或者自己實(shí)現(xiàn)的復(fù)制函數(shù),它們都不是多用途的函數(shù)。通過結(jié)合sizeOf()方法,可以實(shí)現(xiàn)淺復(fù)制。

      static Object shallowCopy(Object obj) {
          long size = sizeOf(obj);
          long start = toAddress(obj);
          long address = getUnsafe().allocateMemory(size);
          getUnsafe().copyMemory(start, address, size);
          return fromAddress(address);
      }

      以下的toAddress()和fromAddress()分別將對象轉(zhuǎn)換到它的地址以及相反操作。

      static long toAddress(Object obj) {
          Object[] array = new Object[] {obj};
          long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
          return normalize(getUnsafe().getInt(array, baseOffset));
      }
       
      static Object fromAddress(long address) {
          Object[] array = new Object[] {null};
          long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);
          getUnsafe().putLong(array, baseOffset, address);
          return array[0];
      }

      以上的淺復(fù)制函數(shù)可以應(yīng)用于任意java對象,它的尺寸是動態(tài)計(jì)算的。

      (5)消去內(nèi)存中的密碼

      密碼字段存儲在String中,但是,String的回收是受到JVM管理的。最安全的做法是,在密碼字段使用完之后,將它的值覆蓋。

      Field stringValue = String.class.getDeclaredField("value");
      stringValue.setAccessible(true);
      char[] mem = (char[]) stringValue.get(password);
      for (int i=0; i < mem.length; i++) {
        mem[i] = '?';
      }

      (6)動態(tài)加載類

      標(biāo)準(zhǔn)的動態(tài)加載類的方法是Class.forName()(在編寫jdbc程序時,記憶深刻),使用Unsafe也可以動態(tài)加載java 的class文件。

      byte[] classContents = getClassContent();
      Class c = getUnsafe().defineClass(
                    null, classContents, 0, classContents.length);
          c.getMethod("a").invoke(c.newInstance(), null); // 1
      getClassContent()方法,將一個class文件,讀取到一個byte數(shù)組。
       
      private static byte[] getClassContent() throws Exception {
          File f = new File("/home/mishadoff/tmp/A.class");
          FileInputStream input = new FileInputStream(f);
          byte[] content = new byte[(int)f.length()];
          input.read(content);
          input.close();
          return content;
      }

      動態(tài)加載、代理、切片等功能中可以應(yīng)用。

      (7)包裝受檢異常為運(yùn)行時異常。

      getUnsafe().throwException(new IOException());

      當(dāng)你不希望捕獲受檢異常時,可以這樣做(并不推薦)。

      (8)快速序列化

      標(biāo)準(zhǔn)的java Serializable速度很慢,它還限制類必須有public無參構(gòu)造函數(shù)。Externalizable好些,它需要為要序列化的類指定模式。流行的高效序列化庫,比如kryo依賴于第三方庫,會增加內(nèi)存的消耗。可以通過getInt(),getLong(),getObject()等方法獲取類中的域的實(shí)際值,將類名稱等信息一起持久化到文件。kryo有使用Unsafe的嘗試,但是沒有具體的性能提升的數(shù)據(jù)。(http://code.google.com/p/kryo/issues/detail?id=75)

      (9)在非Java堆中分配內(nèi)存

      使用java 的new會在堆中為對象分配內(nèi)存,并且對象的生命周期內(nèi),會被JVM GC管理。

      class SuperArray {
          private final static int BYTE = 1;
       
          private long size;
          private long address;
       
          public SuperArray(long size) {
              this.size = size;
              address = getUnsafe().allocateMemory(size * BYTE);
          }
       
          public void set(long i, byte value) {
              getUnsafe().putByte(address + i * BYTE, value);
          }
       
          public int get(long idx) {
              return getUnsafe().getByte(address + idx * BYTE);
          }
       
          public long size() {
              return size;
          }
      }

      Unsafe分配的內(nèi)存,不受Integer.MAX_VALUE的限制,并且分配在非堆內(nèi)存,使用它時,需要非常謹(jǐn)慎:忘記手動回收時,會產(chǎn)生內(nèi)存泄露;非法的地址訪問時,會導(dǎo)致JVM崩潰。在需要分配大的連續(xù)區(qū)域、實(shí)時編程(不能容忍JVM延遲)時,可以使用它。java.nio使用這一技術(shù)。

      (10)Java并發(fā)中的應(yīng)用

      通過使用Unsafe.compareAndSwap()可以用來實(shí)現(xiàn)高效的無鎖數(shù)據(jù)結(jié)構(gòu)。

      class CASCounter implements Counter {
          private volatile long counter = 0;
          private Unsafe unsafe;
          private long offset;
      
          public CASCounter() throws Exception {
              unsafe = getUnsafe();
              offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
          }
      
          @Override
          public void increment() {
              long before = counter;
              while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
                  before = counter;
              }
          }
      
          @Override
          public long getCounter() {
              return counter;
          }
      }

      通過測試,以上數(shù)據(jù)結(jié)構(gòu)與java的原子變量的效率基本一致,Java原子變量也使用Unsafe的compareAndSwap()方法,而這個方法最終會對應(yīng)到cpu的對應(yīng)原語,因此,它的效率非常高。這里有一個實(shí)現(xiàn)無鎖HashMap的方案(http://www.azulsystems.com/about_us/presentations/lock-free-hash ,這個方案的思路是:分析各個狀態(tài),創(chuàng)建拷貝,修改拷貝,使用CAS原語,自旋鎖),在普通的服務(wù)器機(jī)器(核心<32),使用ConcurrentHashMap(JDK8以前,默認(rèn)16路分離鎖實(shí)現(xiàn),JDK8中ConcurrentHashMap已經(jīng)使用無鎖實(shí)現(xiàn))明顯已經(jīng)夠用。

      posted on 2018-05-18 17:26  安卓筆記俠  閱讀(1092)  評論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 丰满少妇被猛烈进出69影院| 99在线视频免费观看| 国内精品无码一区二区三区| 亚洲国产美女精品久久久| 国产超碰无码最新上传| 人妻少妇久久中文字幕| 久热re这里精品视频在线6| 国产一区二区三区麻豆视频| 粉嫩jk制服美女啪啪| 日本阿v片在线播放免费| 一区二区三区av天堂| 国产成人精品无码免费看| 龙岩市| 久久国产精品亚洲精品99| 国产色无码专区在线观看| 成人无码一区二区三区网站| 久久99精品国产麻豆婷婷| 日韩精品一区二区蜜臀av| 日韩国产av一区二区三区精品| 国语精品自产拍在线观看网站| 国产精品无码专区| 91区国产福利在线观看午夜| 日韩在线观看精品亚洲| 精品日本免费一区二区三区| 亚洲午夜av久久久精品影院| 日韩有码av中文字幕| 亚洲精品国产综合麻豆久久99| 久久9精品区-无套内射无码| 一本一道av无码中文字幕麻豆| 亚洲香蕉av一区二区蜜桃| 亚洲熟女乱色一区二区三区| 婷婷四房播播| 久热这里只国产精品视频| 97se亚洲综合在线天天| 亚洲欧洲日韩国内高清| 一区二区三区午夜无码视频| 丰满岳乱妇三级高清 | 国产精品无码无卡在线播放| 蜜臀av一区二区三区不卡| 99热久久这里只有精品| 精品亚洲欧美高清不卡高清|