亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

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

2024-07-14 08:41:00
字體:
來源:轉載
供稿:網友

前言

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

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

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

http://mishadoff.com/blog/javascript/48841.html">java-magic-part-4-sun-dot-misc-dot-unsafe/

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

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

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

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

(4)Arrays相關。數組操縱方法:arrayBaseOffset(),arrayIndexScale()

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

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

2. Unsafe類實例的獲取

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

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

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

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

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

通過將private單例實例暴力設置accessible為true,然后通過Field的get方法,直接獲取一個Object強制轉換為Unsafe。在IDE中,這些方法會被標志為Error,可以通過以下設置解決:

Preferences -> Java -> Compiler -> Errors/Warnings ->Deprecated and restricted API -> Forbidden reference -> Warning

3. Unsafe類“有趣”的應用場景

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

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

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

A o1 = new A(); // constructoro1.a(); // prints 1 A o2 = A.class.newInstance(); // reflectiono2.a(); // prints 1 A o3 = (A) unsafe.allocateInstance(A.class); // unsafeo3.a(); // prints 0

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

(2)內存修改

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

考慮以下簡單準入檢查規則:

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 // bypassUnsafe unsafe = getUnsafe();Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42); // memory corruption guard.giveAccess(); // true, access granted

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

(3)實現類似C語言的sizeOf()函數

通過結合Java反射和objectFieldOffset()函數實現一個C-like sizeOf()函數。

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}

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

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

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

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

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

兩個sizeOf()計算的類的尺寸是一致的。最標準的sizeOf()實現是使用java.lang.instrument,但是,它需要指定命令行參數-javaagent。

(4)實現Java淺復制

標準的淺復制方案是實現Cloneable接口或者自己實現的復制函數,它們都不是多用途的函數。通過結合sizeOf()方法,可以實現淺復制。

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()分別將對象轉換到它的地址以及相反操作。

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];}

以上的淺復制函數可以應用于任意java對象,它的尺寸是動態計算的。

(5)消去內存中的密碼

密碼字段存儲在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)動態加載類

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

byte[] classContents = getClassContent();Class c = getUnsafe().defineClass(    null, classContents, 0, classContents.length); c.getMethod("a").invoke(c.newInstance(), null); // 1getClassContent()方法,將一個class文件,讀取到一個byte數組。 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;}

動態加載、代理、切片等功能中可以應用。

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

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

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

(8)快速序列化

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

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

使用java 的new會在堆中為對象分配內存,并且對象的生命周期內,會被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分配的內存,不受Integer.MAX_VALUE的限制,并且分配在非堆內存,使用它時,需要非常謹慎:忘記手動回收時,會產生內存泄露;非法的地址訪問時,會導致JVM崩潰。在需要分配大的連續區域、實時編程(不能容忍JVM延遲)時,可以使用它。java.nio使用這一技術。

(10)Java并發中的應用

通過使用Unsafe.compareAndSwap()可以用來實現高效的無鎖數據結構。

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; }}

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日本亚洲欧洲色α| 久久精品国产一区二区电影| 精品一区二区三区四区| 国产一区二区精品丝袜| 国产精品视频白浆免费视频| 在线看欧美日韩| 日韩欧美在线一区| 国产精品白嫩初高中害羞小美女| 久久久久久久久久久人体| 亚洲欧美国产精品专区久久| 77777少妇光屁股久久一区| 国产精品香蕉国产| 国产欧美精品日韩| 91精品国产综合久久香蕉| 日本中文字幕久久看| 深夜成人在线观看| 国产性猛交xxxx免费看久久| 国产精品视频1区| 中文字幕视频在线免费欧美日韩综合在线看| 91久久久久久久久久| 亚洲天堂免费在线| 97精品久久久中文字幕免费| xxxxx91麻豆| 精品女同一区二区三区在线播放| 亚洲国产第一页| 亚洲高清在线观看| 91精品啪aⅴ在线观看国产| 成人激情电影一区二区| 欧美激情网站在线观看| 午夜精品在线观看| 色偷偷av一区二区三区乱| 国产成人黄色av| 国产精品久久视频| 久久精品夜夜夜夜夜久久| 国产欧美va欧美va香蕉在线| 高清在线视频日韩欧美| 中国人与牲禽动交精品| 国产精品爽爽ⅴa在线观看| 97热在线精品视频在线观看| 亚州av一区二区| 欧美日韩国产一区二区三区| 精品亚洲一区二区三区| 人人做人人澡人人爽欧美| 亚洲第一精品夜夜躁人人爽| 91在线播放国产| 亚洲男人天堂2023| 欧美一级视频在线观看| 亚洲大尺度美女在线| 久久综合久久88| 色琪琪综合男人的天堂aⅴ视频| 国产精品香蕉av| 国产精品视频一区二区高潮| 成人观看高清在线观看免费| 久久久精品久久久| 不卡伊人av在线播放| 国产精品久久久久久久久久东京| 国产精品久久77777| 日韩中文视频免费在线观看| 欧美激情久久久| 日韩av在线免费播放| 欧美激情xxxx| 国产一区二区欧美日韩| 自拍偷拍亚洲在线| xxxx欧美18另类的高清| 精品久久久av| 欧美精品在线免费| 久久91精品国产91久久跳| 欧美日韩国产精品专区| 91在线免费看网站| 草民午夜欧美限制a级福利片| 国产午夜精品久久久| 亚洲成色777777在线观看影院| 日韩中文字幕久久| 久久久久久国产精品三级玉女聊斋| 亚洲性线免费观看视频成熟| 国产成人短视频| 国产一区在线播放| 国产精品电影观看| 国产午夜精品一区理论片飘花| 91精品国产高清久久久久久| 国产国语刺激对白av不卡| 国产精品自拍偷拍视频| 亚洲欧美国产精品久久久久久久| 国产网站欧美日韩免费精品在线观看| 欧美精品激情视频| 亚洲丝袜一区在线| 精品人伦一区二区三区蜜桃网站| 狠狠色香婷婷久久亚洲精品| 美女国内精品自产拍在线播放| 欧美剧在线观看| 亚洲精品白浆高清久久久久久| 亚洲石原莉奈一区二区在线观看| 91久热免费在线视频| 国产欧洲精品视频| 国产精品电影一区| 91久久久久久久久| 欧美激情视频三区| 亚洲jizzjizz日本少妇| 欧美精品成人91久久久久久久| 欧美人与物videos| 中日韩午夜理伦电影免费| 欧美激情成人在线视频| 成人深夜直播免费观看| 91久久综合亚洲鲁鲁五月天| 在线播放国产精品| 欧美大片欧美激情性色a∨久久| 成人免费看片视频| 亚洲欧美中文日韩在线| 久久久久久久久久婷婷| 日韩中文字幕在线观看| 日韩欧美亚洲综合| 中文字幕av一区| 国产精品99免视看9| 国产精品精品久久久久久| 亚洲午夜国产成人av电影男同| 亚洲人成网站777色婷婷| 国产精品视频一区二区三区四| 成人黄色短视频在线观看| 亚洲无亚洲人成网站77777| 久久这里只有精品视频首页| 亚洲黄页视频免费观看| 国产亚洲精品久久久| 成人国产在线激情| 7777精品久久久久久| 97超碰国产精品女人人人爽| 欧美另类极品videosbest最新版本| 精品免费在线观看| 久久国产视频网站| 日韩成人激情视频| 成人免费高清完整版在线观看| 97超碰国产精品女人人人爽| 国产91精品久久久久| 992tv成人免费视频| 日韩理论片久久| 国产美女精品视频免费观看| 欧美日本中文字幕| 国产精品免费看久久久香蕉| 在线观看免费高清视频97| 日韩成人av网址| 亚洲第五色综合网| 欧美成年人网站| 九九精品在线视频| 色伦专区97中文字幕| 欧美一级淫片aaaaaaa视频| 欧美在线一区二区三区四| 欧美寡妇偷汉性猛交| 亚洲日本欧美日韩高观看| 亚洲2020天天堂在线观看| 日韩精品在线观看一区二区| 日韩精品视频免费专区在线播放| 欧美电影免费观看电视剧大全| 最近的2019中文字幕免费一页| 久久久成人的性感天堂| 欧美在线一区二区视频| 亚洲欧洲av一区二区| 98视频在线噜噜噜国产| 久久人人爽人人爽人人片av高请| 成人妇女免费播放久久久| 中文字幕精品www乱入免费视频| 国产精品国产三级国产aⅴ9色| 亚洲日本成人网| 久久久久久久久中文字幕| 国产成人精品在线视频| 91精品国产综合久久香蕉最新版|