中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

sun?unsafe類功能及使用注意事項是什么

發布時間:2022-01-26 11:51:43 來源:億速云 閱讀:144 作者:柒染 欄目:開發技術

這篇文章跟大家分析一下“sun unsafe類功能及使用注意事項是什么”。內容詳細易懂,對“sun unsafe類功能及使用注意事項是什么”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠對大家有所幫助。下面跟著小編一起深入學習“sun unsafe類功能及使用注意事項是什么”的知識吧。

Unsafe簡介

Unsafe是位于sun.misc包下的一個類,主要提供一些用于執行低級別、不安全操作的方法,如直接訪問系統內存資源、自主管理內存資源等,這些方法在提升Java運行效率、增強Java語言底層資源操作能力方面起到了很大的作用。

但由于Unsafe類使Java語言擁有了類似C語言指針一樣操作內存空間的能力,這無疑也增加了程序發生相關指針問題的風險。

在程序中過度、不正確使用Unsafe類會使得程序出錯的概率變大,使得Java這種安全的語言變得不再“安全”,因此對Unsafe的使用一定要慎重。

獲取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);
        }
    }

Unsafe功能列表

  • 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的數組操作

unsafe中,有兩個關于數組的方法:

public native int arrayBaseOffset(Class<?> arrayClass);
public native int arrayIndexScale(Class<?> arrayClass);

base offset含義

首先,在Java中,數組也是對象

In the Java programming language, arrays are objects (&sect;4.3.1), are dynamically created, and may be assigned to variables of type Object (&sect;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類型和對其方式匹配即可

index scale含義

就是指數組中每個元素所占用的空間大小,比如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);

array copy to direct memory

在使用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);

copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes)

  • 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);

Benchmark

sun.misc.Unsafe#putInt(java.lang.Object, long, int) & object field manual set

禁用JIT結果:

BenchmarkModeCntScoreErrorUnits
ObjectFieldSetBenchmark.manualSetthrpt28646455.472 ops/ns
ObjectFieldSetBenchmark.unsafeSetthrpt27901066.170 ops/ns

啟用JIT結果:

BenchmarkModeCntScoreErrorUnits
ObjectFieldSetBenchmark.manualSetthrpt2477232013.545 ops/ns
ObjectFieldSetBenchmark.unsafeSetthrpt2499135982.962 ops/ns

結論,幾乎沒區別

什么時候用Unsafe

一般使用DirectBuffer時,需要配合Unsafe來使用,因為DirectBuffer的內存是分配在JVM Heap之外的,屬于C Heap,所以需要用直接操作內存地址(邏輯),和C里malloc之后的操作方式一樣

關于sun unsafe類功能及使用注意事項是什么就分享到這里啦,希望上述內容能夠讓大家有所提升。如果想要學習更多知識,請大家多多留意小編的更新。謝謝大家關注一下億速云網站!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

新野县| 长寿区| 崇阳县| 敦煌市| 宁津县| 资兴市| 台前县| 济源市| 垦利县| 深泽县| 武胜县| 上饶县| 勐海县| 萝北县| 沾益县| 阿巴嘎旗| 合阳县| 四平市| 潜山县| 永川市| 黄平县| 临漳县| 永和县| 绥芬河市| 蕲春县| 九江市| 济阳县| 天全县| 乌鲁木齐市| 姚安县| 册亨县| 汨罗市| 兴山县| 萝北县| 叶城县| 略阳县| 左云县| 马尔康县| 永德县| 同江市| 泸西县|