您好,登錄后才能下訂單哦!
這篇文章跟大家分析一下“sun unsafe類功能及使用注意事項是什么”。內容詳細易懂,對“sun unsafe類功能及使用注意事項是什么”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠對大家有所幫助。下面跟著小編一起深入學習“sun unsafe類功能及使用注意事項是什么”的知識吧。
Unsafe是位于sun.misc包下的一個類,主要提供一些用于執行低級別、不安全操作的方法,如直接訪問系統內存資源、自主管理內存資源等,這些方法在提升Java運行效率、增強Java語言底層資源操作能力方面起到了很大的作用。
但由于Unsafe類使Java語言擁有了類似C語言指針一樣操作內存空間的能力,這無疑也增加了程序發生相關指針問題的風險。
在程序中過度、不正確使用Unsafe類會使得程序出錯的概率變大,使得Java這種安全的語言變得不再“安全”,因此對Unsafe的使用一定要慎重。
private static sun.misc.Unsafe getUnsafe() { try { return AccessController.doPrivileged(new PrivilegedExceptionAction<Unsafe>() { @Override public sun.misc.Unsafe run() throws Exception { Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; for (Field f : k.getDeclaredFields()) { f.setAccessible(true); Object x = f.get(null); if (k.isInstance(x)) { return k.cast(x); } } // The sun.misc.Unsafe field does not exist. throw new Error("unsafe is null"); } }); } catch (Throwable e) { throw new Error("get unsafe failed", e); } }
allocateMemory/freeMemory,分配、釋放堆外內存DirectMemory(和c/cpp中的malloc一樣)
CAS操作
copyMemory
defineClass(without security checks)
get/put address 使用堆外內存地址進行數據的讀寫操作
get/put volatile 使用堆外內存地址進行數據的讀寫操作 - volatile版本
loadFence/storeFence/fullFence 禁止指令重排序
park/unpark 阻塞/解除阻塞線程
unsafe中,有兩個關于數組的方法:
public native int arrayBaseOffset(Class<?> arrayClass); public native int arrayIndexScale(Class<?> arrayClass);
首先,在Java中,數組也是對象
In the Java programming language, arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-10.html
那么既然是對象,就會有object header,占用一部分空間,那么理解數組的base offset也就不難了
比如下面的一段JOL輸出,實際上對象的屬性數據是從OFFSET 16的位置開始的,0-12的空間被header所占用
HotSpot 64-bit VM, COOPS, 8-byte alignment lambda.Book object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int Book.sales N/A 16 4 String Book.title N/A 20 4 LocalDate Book.publishTime N/A 24 4 String Book.author N/A 28 4 List<String> Book.tags N/A Instance size: 32 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
那么如果要訪問對象的屬性數據,需要基于基地址(base address)進行偏移,基地址+基礎偏移(base offset)+屬性偏移(field offset)才是數據的內存地址(邏輯),那么title屬性的內存地址實際上就是:
book(instance).title field address = book object base address + base offset + title field offset
數組在Java里可以視為一種特殊的對象,無論什么類型的數組,他們在內存中都會有一分基礎偏移的空間,和object header類似
經過測試,64位的JVM數組類型的基礎偏移都是16(測試結果在不同JVM下可能會有所區別)
原始類型的基礎偏移都是12(測試結果在不同JVM下可能會有所區別)
可以使用JOL工具查看原始類型包裝類的offset,比如int就看Integer的,雖然jdk沒有提供函數,但是通過JOL也是可以獲取的,jvm類型和對其方式匹配即可
就是指數組中每個元素所占用的空間大小,比如int[] scale就是4,long[] scale就是8,object[] scale就是4(指針大小)
有了這個offset,就可以對數組進行copyMemory操作了
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
在使用copyMemory操作時,需要傳入對象及對象的base offset,對于數組來說,offset就是上面介紹的offset,比如現在將一個數組中的數據拷貝至DirectBuffer
byte[] byte = new byte[4096]; unsafe.copyMemory(byte,ARRAY_BYTE_BASE_OFFSET,null,directAddr,4096);
之所以使用unsafe而不是ByteBuffer的方法來操作DirectBuffer,是因為ByteBuffer不夠靈活。
比如我想把一個byte[]拷貝至DirectBuffer的某個位置中,就沒有相應的方法;只能先設置position,然后再put(byte[], int, int),非常麻煩,而且并發訪問時position(long)和put也是個非原子性操作
但是用unsafe來操作的話就很輕松了,直接copyMemory,直接指定address + offset就行
unsafe.copyMemory(byte,ARRAY_BYTE_BASE_OFFSET,null,directAddr,4096);
srcBase 原數據對象,可以是對象、數組(對象),也可以是Null(為Null時必須指定offset,offset就是address)
srcOffset 原數據對象的base offset,如果srcBase為空則此項為address
destBase 目標數據對象,規則同src
destOffset 目標數據對象的base offset,規則同src
bytes 要拷貝的數據大小(字節單位)
通過copyMemory方法,可以做各種拷貝操作:
對象(一般是數組)拷貝到指定堆外內存地址
long l = unsafe.allocateMemory(1); data2[0] = 5; //目標是memory address,destBase為null,destOffset為address unsafe.copyMemory(data2,16,null,l,1);
將對象拷貝到對象
byte[] data1 = new byte[1]; data1[0] = 9; byte[] data2 = new byte[1]; unsafe.copyMemory(data1,16,data2,16,1);
將堆外內存地址的數據拷貝到堆內(一般是數組)
byte[] data2 = new byte[1]; long l = unsafe.allocateMemory(1); unsafe.putByte(l, (byte) 2); //源數據是memory address,srcBase為null,srcOffset為address unsafe.copyMemory(null,l,data2,16,1);
堆外內存地址互相拷貝
long l = unsafe.allocateMemory(1); long l2 = unsafe.allocateMemory(1); unsafe.putByte(l, (byte) 2); //源數據是memory address,srcBase為null,srcOffset為address //目標是memory address,destBase為null,destOffset為address unsafe.copyMemory(null,l,null,l2,1);
sun.misc.Unsafe#putInt(java.lang.Object, long, int) & object field manual set
禁用JIT結果:
Benchmark | Mode | Cnt | Score | Error | Units |
---|---|---|---|---|---|
ObjectFieldSetBenchmark.manualSet | thrpt | 2 | 8646455.472 | ops/ns | |
ObjectFieldSetBenchmark.unsafeSet | thrpt | 2 | 7901066.170 | ops/ns |
啟用JIT結果:
Benchmark | Mode | Cnt | Score | Error | Units |
---|---|---|---|---|---|
ObjectFieldSetBenchmark.manualSet | thrpt | 2 | 477232013.545 | ops/ns | |
ObjectFieldSetBenchmark.unsafeSet | thrpt | 2 | 499135982.962 | ops/ns |
結論,幾乎沒區別
一般使用DirectBuffer時,需要配合Unsafe來使用,因為DirectBuffer的內存是分配在JVM Heap之外的,屬于C Heap,所以需要用直接操作內存地址(邏輯),和C里malloc之后的操作方式一樣
關于sun unsafe類功能及使用注意事項是什么就分享到這里啦,希望上述內容能夠讓大家有所提升。如果想要學習更多知識,請大家多多留意小編的更新。謝謝大家關注一下億速云網站!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。