亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java中的魔法類:sun.misc.Unsafe示例詳解

 更新時間:2018年05月24日 09:07:21   作者:素軒  
Java是一個安全的開發(fā)工具,它阻止開發(fā)人員犯很多低級的錯誤,而大部份的錯誤都是基于內(nèi)存管理方面的。如果你想搞破壞,可以使用Unsafe這個類。下面這篇文章主要給大家介紹了關(guān)于Java中魔法類:sun.misc.Unsafe的相關(guān)資料,需要的朋友可以參考下

前言

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

這個類是屬于sun.* API中的類,并且它不是J2SE中真正的一部份,因此你可能找不到任何的官方文檔,更可悲的是,它也沒有比較好的代碼文檔。

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

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

1. Unsafe API的大部分方法都是native實現(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類實例的獲取

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

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類的雙親加載機制)。

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

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

通過將private單例實例暴力設(shè)置accessible為true,然后通過Field的get方法,直接獲取一個Object強制轉(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()根本沒有進入構(gòu)造方法,在單例模式時,我們似乎看到了危機。

(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

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

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

通過結(jié)合Java反射和objectFieldOffset()函數(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ù)的只計算一次,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()計算的類的尺寸是一致的。最標(biāo)準(zhǔn)的sizeOf()實現(xiàn)是使用java.lang.instrument,但是,它需要指定命令行參數(shù)-javaagent。

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

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

(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)包裝受檢異常為運行時異常。

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

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

(8)快速序列化

標(biāo)準(zhǔn)的java Serializable速度很慢,它還限制類必須有public無參構(gòu)造函數(shù)。Externalizable好些,它需要為要序列化的類指定模式。流行的高效序列化庫,比如kryo依賴于第三方庫,會增加內(nèi)存的消耗。可以通過getInt(),getLong(),getObject()等方法獲取類中的域的實際值,將類名稱等信息一起持久化到文件。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)存,使用它時,需要非常謹慎:忘記手動回收時,會產(chǎn)生內(nèi)存泄露;非法的地址訪問時,會導(dǎo)致JVM崩潰。在需要分配大的連續(xù)區(qū)域、實時編程(不能容忍JVM延遲)時,可以使用它。java.nio使用這一技術(shù)。

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

通過使用Unsafe.compareAndSwap()可以用來實現(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)原語,因此,它的效率非常高。這里有一個實現(xiàn)無鎖HashMap的方案(http://www.azulsystems.com/about_us/presentations/lock-free-hash ,這個方案的思路是:分析各個狀態(tài),創(chuàng)建拷貝,修改拷貝,使用CAS原語,自旋鎖),在普通的服務(wù)器機器(核心<32),使用ConcurrentHashMap(JDK8以前,默認16路分離鎖實現(xiàn),JDK8中ConcurrentHashMap已經(jīng)使用無鎖實現(xiàn))明顯已經(jīng)夠用。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • java中TESTful架構(gòu)原理分析

    java中TESTful架構(gòu)原理分析

    這篇文章主要介紹了對java架構(gòu)中TESTful架構(gòu)原理進行了詳細的原理分析,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進步,早日升職加薪
    2021-09-09
  • Intellij Idea新建SpringBoot項目方式

    Intellij Idea新建SpringBoot項目方式

    這篇文章主要介紹了Intellij Idea新建SpringBoot項目方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • Java經(jīng)典面試題之NIO多路復(fù)用

    Java經(jīng)典面試題之NIO多路復(fù)用

    JAVA?NIO?的多路復(fù)用是面試中經(jīng)常被問的問題,今天我們徹底搞明白究竟是怎么回事,文中的示例代碼講解詳細,希望對大家學(xué)習(xí)Java有所幫助
    2023-06-06
  • Java并發(fā)編程之CountDownLatch解讀

    Java并發(fā)編程之CountDownLatch解讀

    這篇文章主要介紹了Java并發(fā)編程之CountDownLatch解讀,是通過一個計數(shù)器來實現(xiàn)的,計數(shù)器的初始值是線程的數(shù)量,countDownLatch這個類使一個線程等待其他線程各自執(zhí)行完畢后再執(zhí)行,需要的朋友可以參考下
    2023-12-12
  • 如何讓Spring Rest 接口中路徑參數(shù)可選

    如何讓Spring Rest 接口中路徑參數(shù)可選

    這篇文章主要介紹了如何讓Spring Rest 接口中路徑參數(shù)可選,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Springboot使用redisson?+?自定義注解實現(xiàn)消息的發(fā)布訂閱(解決方案)

    Springboot使用redisson?+?自定義注解實現(xiàn)消息的發(fā)布訂閱(解決方案)

    Redisson是一個基于Redis的Java駐留內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory?Data?Grid)和分布式鎖框架,它提供了一系列的分布式Java對象和服務(wù),可以幫助開發(fā)者更方便地使用Redis作為數(shù)據(jù)存儲和分布式鎖的解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • Java11中基于嵌套關(guān)系的訪問控制優(yōu)化詳解

    Java11中基于嵌套關(guān)系的訪問控制優(yōu)化詳解

    Java(和其他語言)通過內(nèi)部類支持嵌套類,要使其正常工作,需要編譯器執(zhí)行一些技巧,下面這篇文章主要給大家介紹了關(guān)于Java11中基于嵌套關(guān)系的訪問控制優(yōu)化的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • 使用idea創(chuàng)建web框架和配置struts的方法詳解

    使用idea創(chuàng)建web框架和配置struts的方法詳解

    這篇文章主要介紹了使用idea創(chuàng)建web框架和配置struts的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • java常用工具類 Date日期、Mail郵件工具類

    java常用工具類 Date日期、Mail郵件工具類

    這篇文章主要為大家詳細介紹了java常用工具類,包括Date日期、Mail郵件工具類,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • Spring?Boot最經(jīng)典的20道面試題你都會了嗎

    Spring?Boot最經(jīng)典的20道面試題你都會了嗎

    Spring Boot是現(xiàn)代化的Java應(yīng)用程序開發(fā)框架,具有高度的靈活性和可擴展性,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot最經(jīng)典的20道面試題,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-06-06

最新評論