成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解Unsafe類

開(kāi)發(fā) 前端
我們介紹了 Unsafe 的基本概念和創(chuàng)建方法,并講了內(nèi)存操作和內(nèi)存屏障兩個(gè)場(chǎng)景。通過(guò)這節(jié)課的學(xué)習(xí),相信大家可以發(fā)現(xiàn),Unsafe 能給我們帶來(lái)實(shí)實(shí)在在的好處。

Unsafe 類位于 sun.misc 包中,sun.misc 包本身在工作中就是個(gè)很少被用到的包。在 Java 的發(fā)展中,sun.misc 包是 Sun 公司早年的內(nèi)部工具包,提供了很多底層操作系統(tǒng)級(jí)別的方法調(diào)用,擁有很大的權(quán)限。然而,大多數(shù)開(kāi)發(fā)手冊(cè)都不推薦使用 sun.misc 包,因?yàn)橹苯邮褂?sun.misc 包下的類,可能會(huì)帶來(lái)安全風(fēng)險(xiǎn)和不可控性。

還記得 Java 和 C 語(yǔ)言相比有什么優(yōu)勢(shì)嗎?

Java 中是沒(méi)有指針的。在程序中維護(hù) C 語(yǔ)言指針的經(jīng)歷一定曾讓你焦頭爛額,而 Java 語(yǔ)言中避免了這種指針操作,這就使得編碼的安全性、效率得到大大地提升。

現(xiàn)在,Java 通過(guò) Unsafe 保留了對(duì)指針的操作能力。這看上去有點(diǎn)前后矛盾,好像說(shuō)不要指針的是 Java,說(shuō)要指針的也是 Java。然而,那么多優(yōu)秀框架底層都用了 Unsafe,那自然是有它適合的場(chǎng)景。

接下來(lái),我們就來(lái)講講 Unsafe 類的創(chuàng)建和它的兩個(gè)常見(jiàn)的應(yīng)用場(chǎng)景。

創(chuàng)建 Unsafe

我們先來(lái)查看一下 Unsafe 的源碼。

public finalclass Unsafe {
privatestaticfinal Unsafe theUnsafe;
  ......
private Unsafe() {
  }
@CallerSensitive
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
      thrownew SecurityException("Unsafe");
    } else {
      return theUnsafe;
    }
  }
}

getUnsafe 似乎可以直接獲取一個(gè) Unsafe 對(duì)象,然而實(shí)際調(diào)用后,getUnsafe 方法一定會(huì)拋出 SecurityException 異常。這是因?yàn)?isSystemDomainLoader 方法會(huì)對(duì)調(diào)用者的 ClassLoader 進(jìn)行檢查,如果調(diào)用者的 ClassLoader 不是 BootStrap ClassLoader,調(diào)用者就會(huì)拋出 SecurityException 異常。

也就是說(shuō),只有 JDK 自己的類才可以使用 getUnsafe 來(lái)獲取 Unsafe 實(shí)例,我們工程師自己的方法是沒(méi)有權(quán)限調(diào)用 getUnsafe 方法的。

這種情況下,我們?nèi)绾潍@取 Unsafe 實(shí)例呢?這里有兩個(gè)方案,我們來(lái)一起看一下。

方案一,利用反射。在 Unsafe 的源碼中,有一個(gè) Unsafe 類型的成員變量——theUnsafe,我們可以通過(guò)反射來(lái)直接獲取這個(gè)變量。

Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);

因?yàn)?theUnsafe 是 private 修飾的,所以我們可以直接用 setAccessible 強(qiáng)制打開(kāi)訪問(wèn)權(quán)限,這樣就繞開(kāi)了層層封鎖,可以直接獲取 Unsafe 對(duì)象了。

方案二,我們可以強(qiáng)制把我們的類放入 BootStrap ClassLoader 的 classpath。JDK 提供了-Xbootclasspath/a 命令允許我們把自己寫的類加入 BootStrap ClassLoader 路徑。這樣就可以直接通過(guò)上面的 getUnsafe 方法獲取 Unsafe 對(duì)象了。

千辛萬(wàn)苦創(chuàng)建了 Unsafe 之后,我們來(lái)繼續(xù)看看 Unsafe 的使用場(chǎng)景。由于 Unsafe 的主要功能是管理內(nèi)存,因此我們就來(lái)一起看看,Unsafe 是如何實(shí)現(xiàn)內(nèi)存操作和內(nèi)存屏障的。

內(nèi)存操作

JVM 強(qiáng)大的一點(diǎn)功能是內(nèi)存的自動(dòng)管理,可以實(shí)現(xiàn)對(duì)象的自動(dòng)回收。然而,一些特殊場(chǎng)景,如 NIO 的直接內(nèi)存,并沒(méi)有走 JVM 的自動(dòng)內(nèi)存管理。Unsafe 允許我們像 C 語(yǔ)言那樣使用指針直接操作內(nèi)存,它的 API 如下:

public native long allocateMemory(long bytes);
public native long reallocateMemory(long address, long bytes);
public native void setMemory(Object o, long offset, long bytes, byte value);
public native void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset,long bytes);
public native void freeMemory(long address);

其中,allocateMemory 是分配內(nèi)存空間,reallocateMemory 方法可以重新調(diào)整內(nèi)存空間大小,setMemory 可以設(shè)置內(nèi)存的值,copyMemory 和 freeMemory 分別是拷貝和清除。這些方法和 C 語(yǔ)言幾乎是對(duì)應(yīng)的。

我們來(lái)看一個(gè)具體的例子吧。運(yùn)行這段代碼,會(huì)輸出什么呢?

long addr = unsafe.allocateMemory(4);
unsafe.setMemory(null,addr ,size,(byte)1);
System.out.println(unsafe.getInt(addr));

輸出的是 16843009。為什么會(huì)這樣呢?

首先,unsafe.allocateMemory(4) 分配了一個(gè) 4 字節(jié)的空間,setMemory 則以 addr 為開(kāi)始,以 addr+size 為結(jié)尾,向每個(gè)字節(jié)分別寫入 1,這時(shí)候的內(nèi)存空間是這樣的:

圖片圖片

getInt 方法會(huì)把結(jié)果轉(zhuǎn)成 10 進(jìn)制并返回,也就是 16843009。

需要注意的是,allocateMemory 分配的是堆外內(nèi)存,是沒(méi)有辦法自動(dòng) GC 的,此時(shí)我們只能手動(dòng)調(diào)用 freeMemory 方法才可以釋放內(nèi)存。對(duì)于上面的代碼,我們可以在 finally 語(yǔ)句塊中調(diào)用 freeMemory 來(lái)釋放 addr。

finally {
        unsafe.freeMemory(addr);
        }

使用堆外內(nèi)存有什么好處呢?

第一個(gè)顯而易見(jiàn)的好處是減少了 GC。數(shù)據(jù)放在堆外內(nèi)存,就和 GC 毫無(wú)關(guān)系了。

其次,提升了 I/O 操作的性能。我們讀取文件或網(wǎng)絡(luò)數(shù)據(jù)的時(shí)候,不可避免地需要在操作系統(tǒng)內(nèi)存和 JVM 內(nèi)存之間拷貝數(shù)據(jù)。雖然拷貝數(shù)據(jù)的這個(gè)過(guò)程是透明的,但占用了一定時(shí)間,直接使用堆外內(nèi)存則減少了一次不必要的內(nèi)存復(fù)制工作,進(jìn)而提升了 I/O 整體性能。我們熟知的 DirectByteBuffer 底層就是基于 Unsafe 實(shí)現(xiàn)的。

內(nèi)存屏障

接下來(lái),我們?cè)賮?lái)看看 Unsafe 類在內(nèi)存屏障場(chǎng)景中的應(yīng)用。

說(shuō)到內(nèi)存屏障,我們就不得不提“指令重排序”了。在多線程中,“指令重排序”是一個(gè)經(jīng)常被提到的概念,簡(jiǎn)單來(lái)說(shuō),就是操作系統(tǒng)在保證輸出結(jié)果正確的情況下,對(duì)你的代碼執(zhí)行順序進(jìn)行調(diào)整,以提升系統(tǒng)執(zhí)行性能。“指令重排序”的弊端在于它可能導(dǎo)致 CPU Cache 和內(nèi)存中的數(shù)據(jù)不一致。

而內(nèi)存屏障是制止重排序的指令,當(dāng)然“指令重排序”的目標(biāo)是為了優(yōu)化執(zhí)行性能,如果二話不說(shuō)直接制止“指令重排序”也是不推薦的。只有當(dāng)“指令重排序”影響正確結(jié)果的情況下,我們才去制止它。Unsafe 提供了下面 3 個(gè)內(nèi)存屏障 API,你看一下:

public native void loadFence();
public native void storeFence();
public native void fullFence();

從名字上看,loadFence 作用于 JVM 的 Load 匯編指令,storeFence 作用于 JVM 的 Store 匯編指令,而 fullFence 同時(shí)會(huì)對(duì) Load 和 Store 生效。對(duì) JVM 匯編指令沒(méi)有了解的同學(xué)可能認(rèn)為 Load 就是讀操作,Store 就是寫操作。

對(duì)于這 3 個(gè) API,我們用個(gè)形象的比喻來(lái)說(shuō)明一下它們的作用吧。假設(shè)你要去做核酸檢測(cè),此時(shí)排起了長(zhǎng)隊(duì),不時(shí)還出現(xiàn)插隊(duì)現(xiàn)象,讓人不堪其擾。于是,你在隊(duì)伍中堆起了一堵高大的墻,墻兩邊的人依然會(huì)出現(xiàn)插隊(duì)現(xiàn)象,但墻一邊的人無(wú)法到達(dá)另一邊,這就是屏障的作用。

換成更專業(yè)的表述就是屏障是一個(gè)同步點(diǎn),使得同步點(diǎn)前的操作必然在同步點(diǎn)后的操作執(zhí)行,同時(shí)屏障會(huì)使得 CPU Cache 中的數(shù)據(jù)失效,強(qiáng)制指令走內(nèi)存讀取數(shù)據(jù)。Java 中的 StampedLock 讀寫鎖,就是使用了內(nèi)存屏障來(lái)實(shí)現(xiàn)的。

總結(jié)

我們介紹了 Unsafe 的基本概念和創(chuàng)建方法,并講了內(nèi)存操作和內(nèi)存屏障兩個(gè)場(chǎng)景。通過(guò)這節(jié)課的學(xué)習(xí),相信大家可以發(fā)現(xiàn),Unsafe 能給我們帶來(lái)實(shí)實(shí)在在的好處。當(dāng)然,Unsafe 如同它的名稱一樣,存在不安全的隱患。然而,直到現(xiàn)在 Unsafe 依然存在。這說(shuō)明,在正確使用的情況下,Unsafe 一定是利大于弊的。

最后講一句,不到萬(wàn)不得已,不要輕易使用 Unsafe。我們講解 Unsafe 是為了讓大家對(duì)底層原理的理解更加深入透徹,至于在生產(chǎn)中應(yīng)用 Unsafe,還要三思而后行。

責(zé)任編輯:武曉燕 來(lái)源: 程序員技術(shù)充電站
相關(guān)推薦

2022-10-12 07:53:46

并發(fā)編程同步工具

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2010-06-01 15:25:27

JavaCLASSPATH

2020-07-21 08:26:08

SpringSecurity過(guò)濾器

2016-01-14 09:38:55

Java加載器理解

2020-09-23 10:00:26

Redis數(shù)據(jù)庫(kù)命令

2019-06-25 10:32:19

UDP編程通信

2017-01-10 08:48:21

2024-02-21 21:14:20

編程語(yǔ)言開(kāi)發(fā)Golang

2025-05-06 00:43:00

MySQL日志文件MIXED 3

2017-08-15 13:05:58

Serverless架構(gòu)開(kāi)發(fā)運(yùn)維

2023-10-19 11:12:15

Netty代碼

2021-02-17 11:25:33

前端JavaScriptthis

2009-09-25 09:14:35

Hibernate日志

2013-09-22 14:57:19

AtWood

2021-09-24 08:10:40

Java 語(yǔ)言 Java 基礎(chǔ)

2012-11-22 13:02:24

jQuery插件Web

2021-11-18 09:20:29

Channel語(yǔ)言代碼

2021-04-22 09:58:15

JDK代理動(dòng)態(tài)

2022-04-24 10:42:59

Kubernete容器網(wǎng)絡(luò)Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美电影免费观看高清 | 国产三级精品三级在线观看四季网 | aaaaaa大片免费看最大的 | 成人不卡| 免费成人高清在线视频 | 日韩伦理一区二区三区 | 亚洲成人一级 | 成人精品在线视频 | 日韩av在线免费 | 国产视频一区二区 | 日韩精品久久久 | 九九国产在线观看 | 亚洲伦理自拍 | 成人h电影在线观看 | 国产欧美在线 | 久久久久久免费毛片精品 | 丝袜 亚洲 欧美 日韩 综合 | 自拍偷拍亚洲一区 | 黄色在线观看网址 | 一区二区三区日韩 | 欧美午夜影院 | 欧美精品久久 | 日韩欧美亚洲 | 欧美激情一区二区三级高清视频 | 亚洲成人综合在线 | 99国内精品久久久久久久 | 亚洲一区免费 | 一级欧美日韩 | 黄色在线免费观看视频 | 成人精品国产 | 久久精品99 | 国产精品亚洲综合 | 亚洲国产精品久久 | 中文字幕av高清 | 国产jizz女人多喷水99 | 欧美爱爱视频 | 欧美视频一区二区三区 | 欧美日韩在线观看视频 | 日本特黄特色aaa大片免费 | 成年人精品视频在线观看 | 日本精品视频在线观看 |