国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中的魔法類:sun.misc.Unsafe示例詳解

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

2021-05-04 14:05素軒 Java教程

Java是一個安全的開發工具,它阻止開發人員犯很多低級的錯誤,而大部份的錯誤都是基于內存管理方面的。如果你想搞破壞,可以使用Unsafe這個類。下面這篇文章主要給大家介紹了關于Java中魔法類:sun.misc.Unsafe的相關資料,需要的

前言

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

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

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

http://mishadoff.com/blog/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信任的啟動類加載器所使用,是一個典型的單例模式類。它的實例獲取方法如下:

?
1
2
3
4
5
6
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反射了。

?
1
2
3
field f = unsafe.class.getdeclaredfield("theunsafe");
f.setaccessible(true);
unsafe unsafe = (unsafe) f.get(null);

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

?
1
2
preferences -> java -> compiler -> errors/warnings ->
deprecated and restricted api -> forbidden reference -> warning

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

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

?
1
2
3
4
5
6
7
class a {
 private long a; // not initialized value
 public a() {
  this.a = 1; // initialization
 }
 public long a() { return this.a; }
}

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

?
1
2
3
4
5
6
7
8
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()根本沒有進入構造方法,在單例模式時,我們似乎看到了危機。

(2)內存修改

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

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

?
1
2
3
4
5
6
7
class guard {
 private int access_allowed = 1;
 
 public boolean giveaccess() {
  return 42 == access_allowed;
 }
}

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

?
1
2
3
4
5
6
7
8
9
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

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

(3)實現類似c語言的sizeof()函數

通過結合java反射和objectfieldoffset()函數實現一個c-like sizeof()函數。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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。

?
1
2
3
4
public static long sizeof(object object){
 return getunsafe().getaddress(
  normalize(getunsafe().getint(object, 4l)) + 12l);
}

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

?
1
2
3
4
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()方法,可以實現淺復制。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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管理的。最安全的做法是,在密碼字段使用完之后,將它的值覆蓋。

?
1
2
3
4
5
6
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文件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
byte[] classcontents = getclasscontent();
class c = getunsafe().defineclass(
    null, classcontents, 0, classcontents.length);
 c.getmethod("a").invoke(c.newinstance(), null); // 1
getclasscontent()方法,將一個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)包裝受檢異常為運行時異常。

?
1
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管理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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()可以用來實現高效的無鎖數據結構。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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已經使用無鎖實現)明顯已經夠用。

總結

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

原文鏈接:https://www.cnblogs.com/suxuan/p/4948608.html

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 国产婷婷| 国产中文视频 | 欧美在线免费视频 | 日韩一区二区精品视频 | 日韩av在线中文字幕 | 成人高清网站 | 亚洲成a人片在线 | 最好的2019中文大全在线观看 | 精品一区二区在线看 | 日韩在线观看中文 | 久久久久久不卡 | 免费日本视频 | 久久久婷| 中文字幕91| 中文在线一区二区三区 | av网站观看 | 色五月激情五月 | 99精品视频免费观看 | 日韩精品一区二区三区在线播放 | 精品欧美一区二区三区久久久 | 国产精品国产精品国产专区不片 | 日韩精品91爱爱 | 欧美a一级| 91精品一区二区三区久久久久久 | 国产一区二区精品在线 | 日韩成人精品 | 亚洲国产高清在线 | 欧美成人综合在线 | 国产2区 | av免费网站在线观看 | 视频在线一区二区三区 | 亚洲少妇视频 | 亚洲在线视频 | 欧美成人免费 | 亚洲黄色片免费看 | 狠狠爱亚洲 | 毛片区 | 激情综合在线 | 国产精品综合一区二区 | 亚洲精品短视频 | 美女一级毛片 |