java中Unsafe的使用講解
前段時(shí)間因?yàn)榭碕UC的源碼,里面有大量關(guān)于unsafe的操作,所以就來(lái)看看了.寫(xiě)點(diǎn)筆記總結(jié)下(本文基于jdk1.8):
unsafe可以幫我們直接去操作硬件資源,當(dāng)然了是借助java的jit來(lái)進(jìn)行的,官方不推薦使用,因?yàn)椴话踩?,例如你使用unsafe創(chuàng)建一個(gè)超級(jí)大的數(shù)組,但是這個(gè)數(shù)組jvm是不管理的,只能你自己操作,容易o(hù)om,也不利于資源的回收。
好了,下面我們來(lái)看代碼
1.獲取unsafe
//1.最簡(jiǎn)單的使用方式是基于反射獲取Unsafe實(shí)例
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
2.獲取unsafe
private static Unsafe unsafe = null;
private static Field getUnsafe = null;
static {
try {
getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
getUnsafe.setAccessible(true);
unsafe = (Unsafe) getUnsafe.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
隨便只要你高興,都可以獲取到unfase,因?yàn)樯婕暗絬nfase 的權(quán)限問(wèn)題,所以,我們只能使用這種方式獲取,不然就是權(quán)限異常,
操作方法:
/**
* 操作數(shù)組:
* 可以獲取數(shù)組的在內(nèi)容中的基本偏移量(arrayBaseOffset),獲取數(shù)組內(nèi)元素的間隔(比例),
* 根據(jù)數(shù)組對(duì)象和偏移量獲取元素值(getObject),設(shè)置數(shù)組元素值(putObject),示例如下。
*/
String[] strings = new String[]{"1", "2", "3"};
long i = unsafe.arrayBaseOffset(String[].class);
System.out.println("string[] base offset is :" + i);
//every index scale
long scale = unsafe.arrayIndexScale(String[].class);
System.out.println("string[] index scale is " + scale);
//print first string in strings[]
System.out.println("first element is :" + unsafe.getObject(strings, i));
//set 100 to first string
unsafe.putObject(strings, i + scale * 0, "100");
//print first string in strings[] again
System.out.println("after set ,first element is :" + unsafe.getObject(strings, i + scale * 0));
/**
* 對(duì)象操作
* 實(shí)例化Data
*
* 可以通過(guò)類的class對(duì)象創(chuàng)建類對(duì)象(allocateInstance),獲取對(duì)象屬性的偏移量(objectFieldOffset)
* ,通過(guò)偏移量設(shè)置對(duì)象的值(putObject)
*
* 對(duì)象的反序列化
* 當(dāng)使用框架反序列化或者構(gòu)建對(duì)象時(shí),會(huì)假設(shè)從已存在的對(duì)象中重建,你期望使用反射來(lái)調(diào)用類的設(shè)置函數(shù),
* 或者更準(zhǔn)確一點(diǎn)是能直接設(shè)置內(nèi)部字段甚至是final字段的函數(shù)。問(wèn)題是你想創(chuàng)建一個(gè)對(duì)象的實(shí)例,
* 但你實(shí)際上又不需要構(gòu)造函數(shù),因?yàn)樗赡軙?huì)使問(wèn)題更加困難而且會(huì)有副作用。
*
*/
//調(diào)用allocateInstance函數(shù)避免了在我們不需要構(gòu)造函數(shù)的時(shí)候卻調(diào)用它
Data data = (Data) unsafe.allocateInstance(Data.class);
data.setId(1L);
data.setName("unsafe");
System.out.println(data);
//返回成員屬性在內(nèi)存中的地址相對(duì)于對(duì)象內(nèi)存地址的偏移量
Field nameField = Data.class.getDeclaredField("name");
long fieldOffset = unsafe.objectFieldOffset(nameField);
//putLong,putInt,putDouble,putChar,putObject等方法,直接修改內(nèi)存數(shù)據(jù)(可以越過(guò)訪問(wèn)權(quán)限)
unsafe.putObject(data,fieldOffset,"這是新的值");
System.out.println(data.getName());
/**
* 我們可以在運(yùn)行時(shí)創(chuàng)建一個(gè)類,比如從已編譯的.class文件中。將類內(nèi)容讀取為字節(jié)數(shù)組,
* 并正確地傳遞給defineClass方法;當(dāng)你必須動(dòng)態(tài)創(chuàng)建類,而現(xiàn)有代碼中有一些代理, 這是很有用的
*/
File file = new File("C:\\workspace\\idea2\\disruptor\\target\\classes\\com\\onyx\\distruptor\\test\\Data.class");
FileInputStream input = new FileInputStream(file);
byte[] content = new byte[(int)file.length()];
input.read(content);
Class c = unsafe.defineClass(null, content, 0, content.length,null,null);
c.getMethod("getId").invoke(c.newInstance(), null);
/**
* 內(nèi)存操作
* 可以在Java內(nèi)存區(qū)域中分配內(nèi)存(allocateMemory),設(shè)置內(nèi)存(setMemory,用于初始化),
* 在指定的內(nèi)存位置中設(shè)置值(putInt\putBoolean\putDouble等基本類型)
*/
//分配一個(gè)8byte的內(nèi)存
long address = unsafe.allocateMemory(8L);
//初始化內(nèi)存填充1
unsafe.setMemory(address, 8L, (byte) 1);
//測(cè)試輸出
System.out.println("add byte to memory:" + unsafe.getInt(address));
//設(shè)置0-3 4個(gè)byte為0x7fffffff
unsafe.putInt(address, 0x7fffffff);
//設(shè)置4-7 4個(gè)byte為0x80000000
unsafe.putInt(address + 4, 0x80000000);
//int占用4byte
System.out.println("add byte to memory:" + unsafe.getInt(address));
System.out.println("add byte to memory:" + unsafe.getInt(address + 4));
/**
* CAS操作
* Compare And Swap(比較并交換),當(dāng)需要改變的值為期望的值時(shí),那么就替換它為新的值,是原子
* (不可在分割)的操作。很多并發(fā)框架底層都用到了CAS操作,CAS操作優(yōu)勢(shì)是無(wú)鎖,可以減少線程切換耗費(fèi)
* 的時(shí)間,但CAS經(jīng)常失敗運(yùn)行容易引起性能問(wèn)題,也存在ABA問(wèn)題。在Unsafe中包含compareAndSwapObject、
* compareAndSwapInt、compareAndSwapLong三個(gè)方法,compareAndSwapInt的簡(jiǎn)單示例如下。
*/
Data data = new Data();
data.setId(1L);
Field id = data.getClass().getDeclaredField("id");
long l = unsafe.objectFieldOffset(id);
id.setAccessible(true);
//比較并交換,比如id的值如果是所期望的值1,那么就替換為2,否則不做處理
unsafe.compareAndSwapLong(data,1L,1L,2L);
System.out.println(data.getId());
/**
* 常量獲取
*
* 可以獲取地址大?。╝ddressSize),頁(yè)大?。╬ageSize),基本類型數(shù)組的偏移量
* (Unsafe.ARRAY_INT_BASE_OFFSET\Unsafe.ARRAY_BOOLEAN_BASE_OFFSET等)、
* 基本類型數(shù)組內(nèi)元素的間隔(Unsafe.ARRAY_INT_INDEX_SCALE\Unsafe.ARRAY_BOOLEAN_INDEX_SCALE等)
*/
//get os address size
System.out.println("address size is :" + unsafe.addressSize());
//get os page size
System.out.println("page size is :" + unsafe.pageSize());
//int array base offset
System.out.println("unsafe array int base offset:" + Unsafe.ARRAY_INT_BASE_OFFSET);
/**
* 線程許可
* 許可線程通過(guò)(park),或者讓線程等待許可(unpark),
*/
Thread packThread = new Thread(() -> {
long startTime = System.currentTimeMillis();
//納秒,相對(duì)時(shí)間park
unsafe.park(false,3000000000L);
//毫秒,絕對(duì)時(shí)間park
//unsafe.park(true,System.currentTimeMillis()+3000);
System.out.println("main thread end,cost :"+(System.currentTimeMillis()-startTime)+"ms");
});
packThread.start();
TimeUnit.SECONDS.sleep(1);
//注釋掉下一行后,線程3秒數(shù)后進(jìn)行輸出,否則在1秒后輸出
unsafe.unpark(packThread);
/**
* Java數(shù)組大小的最大值為Integer.MAX_VALUE。使用直接內(nèi)存分配,我們創(chuàng)建的數(shù)組大小受限于堆大??;
* 實(shí)際上,這是堆外內(nèi)存(off-heap memory)技術(shù),在java.nio包中部分可用;
*
* 這種方式的內(nèi)存分配不在堆上,且不受GC管理,所以必須小心Unsafe.freeMemory()的使用。
* 它也不執(zhí)行任何邊界檢查,所以任何非法訪問(wèn)可能會(huì)導(dǎo)致JVM崩潰
*/
public class SuperArray {
private static Unsafe unsafe = null;
private static Field getUnsafe = null;
static {
try {
getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
getUnsafe.setAccessible(true);
unsafe = (Unsafe) getUnsafe.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private final static int BYTE = 1;
private long size;
private long address;
public SuperArray(long size) {
this.size = size;
address = unsafe.allocateMemory(size * BYTE);
}
public void set(long i, byte value) {
unsafe.putByte(address + i * BYTE, value);
}
public int get(long idx) {
return unsafe.getByte(address + idx * BYTE);
}
public long size() {
return size;
}
public static void main(String[] args) {
long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;
SuperArray array = new SuperArray(SUPER_SIZE);
System.out.println("Array size:" + array.size()); // 4294967294
int sum=0;
for (int i = 0; i < 100; i++) {
array.set((long)Integer.MAX_VALUE + i, (byte)3);
sum += array.get((long)Integer.MAX_VALUE + i);
}
System.out.println(sum);
}
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java和Ceylon對(duì)象的構(gòu)造和驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了Java和Ceylon對(duì)象的構(gòu)造和驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過(guò)代理類的問(wèn)題
這篇文章主要介紹了解決SpringAop內(nèi)部調(diào)用時(shí)不經(jīng)過(guò)代理類的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
java連接mysql數(shù)據(jù)庫(kù)及測(cè)試是否連接成功的方法
這篇文章主要介紹了java連接mysql數(shù)據(jù)庫(kù)及測(cè)試是否連接成功的方法,結(jié)合完整實(shí)例形式分析了java基于jdbc連接mysql數(shù)據(jù)庫(kù)并返回連接狀態(tài)的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
IDEA 顯示Run Dashboard窗口的2種方式(推薦)
這篇文章主要介紹了IDEA 顯示Run Dashboard窗口的2種方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能示例
這篇文章主要介紹了Java實(shí)現(xiàn)文件和base64流的相互轉(zhuǎn)換功能,涉及Java文件讀取及base64 轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
SpringBatch數(shù)據(jù)讀取的實(shí)現(xiàn)(ItemReader與自定義讀取邏輯)
本文主要介紹了SpringBatch數(shù)據(jù)讀取的實(shí)現(xiàn), 文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-04-04
SpringBoot框架RESTful接口設(shè)置跨域允許
這篇文章主要為大家詳細(xì)介紹了SpringBoot框架RESTful接口設(shè)置跨域允許,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08

