前段時間因為看JUC的源碼,裡面有大量關于unsafe的操作,是以就來看看了.寫點筆記總結下(本文基于jdk1.8):
unsafe可以幫我們直接去操作硬體資源,當然了是借助java的jit來進行的,官方不推薦使用,因為不安全,例如你使用unsafe建立一個超級大的數組,但是這個數組jvm是不管理的,隻能你自己操作,容易oom,也不利于資源的回收.
好了,下面我們來看代碼,
1.擷取unsafe
-
//1.最簡單的使用方式是基于反射擷取Unsafe執行個體
-
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,因為涉及到unfase 的權限問題,是以,我們隻能使用這種方式擷取,不然就是權限異常,
操作方法:
-
/**
-
* 操作數組:
-
* 可以擷取數組的在内容中的基本偏移量(arrayBaseOffset),擷取數組内元素的間隔(比例),
-
* 根據數組對象和偏移量擷取元素值(getObject),設定數組元素值(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));
-
/**
-
* 對象操作
-
* 執行個體化Data
-
*
-
* 可以通過類的class對象建立類對象(allocateInstance),擷取對象屬性的偏移量(objectFieldOffset)
-
* ,通過偏移量設定對象的值(putObject)
-
*
-
* 對象的反序列化
-
* 當使用架構反序列化或者建構對象時,會假設從已存在的對象中重建,你期望使用反射來調用類的設定函數,
-
* 或者更準确一點是能直接設定内部字段甚至是final字段的函數。問題是你想建立一個對象的執行個體,
-
* 但你實際上又不需要構造函數,因為它可能會使問題更加困難而且會有副作用。
-
*
-
*/
-
//調用allocateInstance函數避免了在我們不需要構造函數的時候卻調用它
-
Data data = (Data) unsafe.allocateInstance(Data.class);
-
data.setId(1L);
-
data.setName("unsafe");
-
System.out.println(data);
-
//傳回成員屬性在記憶體中的位址相對于對象記憶體位址的偏移量
-
Field nameField = Data.class.getDeclaredField("name");
-
long fieldOffset = unsafe.objectFieldOffset(nameField);
-
//putLong,putInt,putDouble,putChar,putObject等方法,直接修改記憶體資料(可以越過通路權限)
-
unsafe.putObject(data,fieldOffset,"這是新的值");
-
System.out.println(data.getName());
-
/**
-
* 我們可以在運作時建立一個類,比如從已編譯的.class檔案中。将類内容讀取為位元組數組,
-
* 并正确地傳遞給defineClass方法;當你必須動态建立類,而現有代碼中有一些代理, 這是很有用的
-
*/
-
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);
-
/**
-
* 記憶體操作
-
* 可以在Java記憶體區域中配置設定記憶體(allocateMemory),設定記憶體(setMemory,用于初始化),
-
* 在指定的記憶體位置中設定值(putInt\putBoolean\putDouble等基本類型)
-
*/
-
//配置設定一個8byte的記憶體
-
long address = unsafe.allocateMemory(8L);
-
//初始化記憶體填充1
-
unsafe.setMemory(address, 8L, (byte) 1);
-
//測試輸出
-
System.out.println("add byte to memory:" + unsafe.getInt(address));
-
//設定0-3 4個byte為0x7fffffff
-
unsafe.putInt(address, 0x7fffffff);
-
//設定4-7 4個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(比較并交換),當需要改變的值為期望的值時,那麼就替換它為新的值,是原子
-
* (不可在分割)的操作。很多并發架構底層都用到了CAS操作,CAS操作優勢是無鎖,可以減少線程切換耗費
-
* 的時間,但CAS經常失敗運作容易引起性能問題,也存在ABA問題。在Unsafe中包含compareAndSwapObject、
-
* compareAndSwapInt、compareAndSwapLong三個方法,compareAndSwapInt的簡單示例如下。
-
*/
-
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());
-
/**
-
* 常量擷取
-
*
-
* 可以擷取位址大小(addressSize),頁大小(pageSize),基本類型數組的偏移量
-
* (Unsafe.ARRAY_INT_BASE_OFFSET\Unsafe.ARRAY_BOOLEAN_BASE_OFFSET等)、
-
* 基本類型數組内元素的間隔(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);
-
/**
-
* 線程許可
-
* 許可線程通過(park),或者讓線程等待許可(unpark),
-
*/
-
Thread packThread = new Thread(() -> {
-
long startTime = System.currentTimeMillis();
-
//納秒,相對時間park
-
unsafe.park(false,3000000000L);
-
//毫秒,絕對時間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秒數後進行輸出,否則在1秒後輸出
-
unsafe.unpark(packThread);
-
/**
-
* Java數組大小的最大值為Integer.MAX_VALUE。使用直接記憶體配置設定,我們建立的數組大小受限于堆大小;
-
* 實際上,這是堆外記憶體(off-heap memory)技術,在java.nio包中部分可用;
-
*
-
* 這種方式的記憶體配置設定不在堆上,且不受GC管理,是以必須小心Unsafe.freeMemory()的使用。
-
* 它也不執行任何邊界檢查,是以任何非法通路可能會導緻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);
-
}
-
}