溫馨提示×

溫馨提示×

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

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

sun?unsafe類功能及使用注意事項(xiàng)是什么

發(fā)布時(shí)間:2022-01-26 11:51:43 來源:億速云 閱讀:143 作者:柒染 欄目:開發(fā)技術(shù)

這篇文章跟大家分析一下“sun unsafe類功能及使用注意事項(xiàng)是什么”。內(nèi)容詳細(xì)易懂,對“sun unsafe類功能及使用注意事項(xiàng)是什么”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠?qū)Υ蠹矣兴鶐椭O旅娓【幰黄鹕钊雽W(xué)習(xí)“sun unsafe類功能及使用注意事項(xiàng)是什么”的知識(shí)吧。

Unsafe簡介

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

但由于Unsafe類使Java語言擁有了類似C語言指針一樣操作內(nèi)存空間的能力,這無疑也增加了程序發(fā)生相關(guān)指針問題的風(fēng)險(xiǎn)。

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

獲取Unsafe實(shí)例

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,分配、釋放堆外內(nèi)存DirectMemory(和c/cpp中的malloc一樣)

  • CAS操作

  • copyMemory

  • defineClass(without security checks)

  • get/put address 使用堆外內(nèi)存地址進(jìn)行數(shù)據(jù)的讀寫操作

  • get/put volatile 使用堆外內(nèi)存地址進(jìn)行數(shù)據(jù)的讀寫操作 - volatile版本

  • loadFence/storeFence/fullFence 禁止指令重排序

  • park/unpark 阻塞/解除阻塞線程

Unsafe的數(shù)組操作

unsafe中,有兩個(gè)關(guān)于數(shù)組的方法:

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

base offset含義

首先,在Java中,數(shù)組也是對象

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

那么既然是對象,就會(huì)有object header,占用一部分空間,那么理解數(shù)組的base offset也就不難了

比如下面的一段JOL輸出,實(shí)際上對象的屬性數(shù)據(jù)是從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

那么如果要訪問對象的屬性數(shù)據(jù),需要基于基地址(base address)進(jìn)行偏移,基地址+基礎(chǔ)偏移(base offset)+屬性偏移(field offset)才是數(shù)據(jù)的內(nèi)存地址(邏輯),那么title屬性的內(nèi)存地址實(shí)際上就是:

book(instance).title field address = book object base address + base offset + title field offset

數(shù)組在Java里可以視為一種特殊的對象,無論什么類型的數(shù)組,他們在內(nèi)存中都會(huì)有一分基礎(chǔ)偏移的空間,和object header類似

經(jīng)過測試,64位的JVM數(shù)組類型的基礎(chǔ)偏移都是16(測試結(jié)果在不同JVM下可能會(huì)有所區(qū)別)
原始類型的基礎(chǔ)偏移都是12(測試結(jié)果在不同JVM下可能會(huì)有所區(qū)別)
可以使用JOL工具查看原始類型包裝類的offset,比如int就看Integer的,雖然jdk沒有提供函數(shù),但是通過JOL也是可以獲取的,jvm類型和對其方式匹配即可

index scale含義

就是指數(shù)組中每個(gè)元素所占用的空間大小,比如int[] scale就是4,long[] scale就是8,object[] scale就是4(指針大?。?/p>

有了這個(gè)offset,就可以對數(shù)組進(jìn)行copyMemory操作了

public native void copyMemory(Object srcBase, long srcOffset,
                                  Object destBase, long destOffset,
                                  long bytes);

array copy to direct memory

在使用copyMemory操作時(shí),需要傳入對象及對象的base offset,對于數(shù)組來說,offset就是上面介紹的offset,比如現(xiàn)在將一個(gè)數(shù)組中的數(shù)據(jù)拷貝至DirectBuffer

byte[] byte = new byte[4096];
unsafe.copyMemory(byte,ARRAY_BYTE_BASE_OFFSET,null,directAddr,4096);

之所以使用unsafe而不是ByteBuffer的方法來操作DirectBuffer,是因?yàn)锽yteBuffer不夠靈活。

比如我想把一個(gè)byte[]拷貝至DirectBuffer的某個(gè)位置中,就沒有相應(yīng)的方法;只能先設(shè)置position,然后再put(byte[], int, int),非常麻煩,而且并發(fā)訪問時(shí)position(long)和put也是個(gè)非原子性操作

但是用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 原數(shù)據(jù)對象,可以是對象、數(shù)組(對象),也可以是Null(為Null時(shí)必須指定offset,offset就是address)

  • srcOffset 原數(shù)據(jù)對象的base offset,如果srcBase為空則此項(xiàng)為address

  • destBase 目標(biāo)數(shù)據(jù)對象,規(guī)則同src

  • destOffset 目標(biāo)數(shù)據(jù)對象的base offset,規(guī)則同src

  • bytes 要拷貝的數(shù)據(jù)大?。ㄗ止?jié)單位)

通過copyMemory方法,可以做各種拷貝操作:

對象(一般是數(shù)組)拷貝到指定堆外內(nèi)存地址

long l = unsafe.allocateMemory(1);
data2[0] = 5;
//目標(biāo)是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);

將堆外內(nèi)存地址的數(shù)據(jù)拷貝到堆內(nèi)(一般是數(shù)組)

byte[] data2 = new byte[1];

long l = unsafe.allocateMemory(1);
unsafe.putByte(l, (byte) 2);
//源數(shù)據(jù)是memory address,srcBase為null,srcOffset為address
unsafe.copyMemory(null,l,data2,16,1);

堆外內(nèi)存地址互相拷貝

long l = unsafe.allocateMemory(1);
long l2 = unsafe.allocateMemory(1);
unsafe.putByte(l, (byte) 2);
//源數(shù)據(jù)是memory address,srcBase為null,srcOffset為address
//目標(biāo)是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結(jié)果:

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

啟用JIT結(jié)果:

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

結(jié)論,幾乎沒區(qū)別

什么時(shí)候用Unsafe

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

關(guān)于sun unsafe類功能及使用注意事項(xiàng)是什么就分享到這里啦,希望上述內(nèi)容能夠讓大家有所提升。如果想要學(xué)習(xí)更多知識(shí),請大家多多留意小編的更新。謝謝大家關(guān)注一下億速云網(wǎng)站!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI