偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

淺談JVM運(yùn)行期的幾種優(yōu)化手段

開(kāi)發(fā) 前端
Java 中最典型的聚合量是對(duì)象,如果逃逸分析證明一個(gè)對(duì)象不會(huì)被外部訪問(wèn),并且這個(gè)對(duì)象是可分解的,那程序真正執(zhí)行的時(shí)候?qū)⒖赡懿粍?chuàng)建這個(gè)對(duì)象,而改為直接創(chuàng)建它的若干個(gè)被這個(gè)方法使用到的成員變量來(lái)代替,拆散后的變量便可以被單獨(dú)分析與優(yōu)化,可以各自分別在棧幀或寄存器上分配空間,原本的對(duì)象就無(wú)需整體分配空間了。

一、摘要

在之前的文章中我們談到過(guò),相比 C/C++ 語(yǔ)言,Java 語(yǔ)言在運(yùn)行效率方面要稍遜一些,因?yàn)?Java 應(yīng)用程序是在虛擬機(jī)上運(yùn)行,而 C/C++ 程序是直接編譯成平臺(tái)相應(yīng)的機(jī)器碼來(lái)運(yùn)行程序。

從虛擬機(jī)對(duì)外發(fā)布開(kāi)始,開(kāi)發(fā)團(tuán)隊(duì)一直在努力試圖縮小 Java 與 C/C++ 語(yǔ)言在運(yùn)行效率上的差距。從實(shí)際的結(jié)果來(lái)看,確實(shí)成果顯著。

本文就來(lái)聊聊 HotSpot 虛擬機(jī)為了提升 Java 程序的執(zhí)行效率,都實(shí)現(xiàn)了哪些激動(dòng)人心的優(yōu)化技術(shù)。

二、JIT 編譯器的引入

JIT 編譯器,也稱為即時(shí)編譯器,它是 JVM 的重要組成部分。與我們經(jīng)常用的生成 Java 字節(jié)碼的javac編譯器不同,JIT 編譯器是實(shí)現(xiàn) Java 程序執(zhí)行效率提升的核心利器。

經(jīng)常有面試官會(huì)提出這樣的一個(gè)問(wèn)題:Java 程序是解釋執(zhí)行還是編譯執(zhí)行?

剛開(kāi)始學(xué)習(xí) Java 的同學(xué),大概率會(huì)認(rèn)為 Java 是編譯執(zhí)行,其執(zhí)行流程類似于如下圖。

圖片圖片

源碼程序.java文件,通過(guò)javac命令編譯成.class字節(jié)碼,最后通過(guò)java命令在虛擬機(jī)中利用解釋器來(lái)執(zhí)行代碼。其中虛擬機(jī)的解釋器作用,就是將字節(jié)碼的操作指令和真正的平臺(tái)體系之間的指令建立映射,比如把 Java 的load指令轉(zhuǎn)換成native code的load指令,以此來(lái)完成程序的執(zhí)行。

其實(shí),準(zhǔn)確的說(shuō),Java 既有解釋執(zhí)行,也有編譯執(zhí)行,其工作流程大致可以用如下圖來(lái)描述。

圖片圖片

其中,JIT 編譯器會(huì)將熱點(diǎn)代碼編譯成本地平臺(tái)相關(guān)的機(jī)器碼,并進(jìn)行各種層次的優(yōu)化,從而實(shí)現(xiàn)程序執(zhí)行效率的提升。

JIT 編譯器的出現(xiàn),可以說(shuō)補(bǔ)強(qiáng)了虛擬機(jī)邊運(yùn)行邊解釋的低性能問(wèn)題。

也許有的同學(xué)會(huì)提出這樣的疑問(wèn),既然引入了 JIT 編譯器可以顯著提升程序執(zhí)行效率,那 HotSpot 為什么不直接采用 JIT 編譯器來(lái)執(zhí)行呢?

簡(jiǎn)單的說(shuō),解釋器和編譯器各有優(yōu)勢(shì)。

  • 當(dāng)程序需要迅速啟動(dòng)和執(zhí)行時(shí),解釋器可以首先發(fā)揮作用,省去編譯的時(shí)間,可以立即執(zhí)行
  • 當(dāng)程序運(yùn)行后,隨著時(shí)間的推移,JIT 編譯器可以發(fā)揮作用,能把越來(lái)越多的代碼編譯成本地機(jī)器碼,進(jìn)一步提升程序的執(zhí)行效率

這就是為什么 Java 程序既有解釋執(zhí)行,也有編譯執(zhí)行的原因。

當(dāng)然,能觸發(fā)即時(shí)編譯請(qǐng)求的條件比較多,比如方法調(diào)用,OSR 編譯請(qǐng)求等。在默認(rèn)設(shè)置下,無(wú)論是哪種場(chǎng)景,虛擬機(jī)在代碼編譯器還未完成的時(shí)候,都仍然按照解釋器來(lái)繼續(xù)執(zhí)行,而編譯動(dòng)作則是在后臺(tái)的編譯線程中運(yùn)行。

用戶可以通過(guò)-XX:-BackgroundCompilation參數(shù)來(lái)禁止后臺(tái)編譯,此時(shí)所有的編譯請(qǐng)求會(huì)等待,直到編譯完成后再開(kāi)始執(zhí)行本地機(jī)器碼。

2.1、Client 模式與 Server 模式

在 HotSpot 虛擬機(jī)中內(nèi)置了兩款即時(shí)編譯器,分別是Client Compiler和Server Compiler,也稱為 C1 編譯器與 C2 編譯器。

在目前的 HotSpot 虛擬機(jī)中,默認(rèn)采用的是解釋器與其中一個(gè)即時(shí)編譯器直接配合的工作方式,用戶也可以使用-client或者-server參數(shù)來(lái)指定解釋器與具體的某個(gè)編譯器配合工作。

它們之間的區(qū)別,可以用如下內(nèi)容簡(jiǎn)要概括:

  • Client Compiler(C1編譯器):它是一個(gè)簡(jiǎn)單快速的編譯器,主要關(guān)注點(diǎn)在于局部性的優(yōu)化,而放棄了許多耗時(shí)間長(zhǎng)的全局優(yōu)化手段
  • Sever Compiler(C2編譯器):它是專門(mén)面向服務(wù)端的典型應(yīng)用并為服務(wù)端的性能配置特別調(diào)整過(guò)的編譯器,它會(huì)執(zhí)行所有經(jīng)典的優(yōu)化動(dòng)作,如無(wú)用代碼消除、循環(huán)展開(kāi)、常量傳播、基本塊重排序等,還會(huì)實(shí)施一些與 Java 語(yǔ)言特性密切相關(guān)的優(yōu)化技術(shù),如范圍檢查消除、空值檢查消除等,另外,還有可能根據(jù)解釋器或 Client Compiler 提供的性能監(jiān)控信息,進(jìn)行一些不穩(wěn)定的激進(jìn)優(yōu)化,如守護(hù)內(nèi)聯(lián)、分支頻率預(yù)測(cè)等

Sever Compiler 即時(shí)編譯器,無(wú)疑是比較緩慢的,但它的編譯速度依然遠(yuǎn)超傳統(tǒng)的靜態(tài)優(yōu)化編譯器,而且它相對(duì)于 Client Compiler 編譯器輸出的代碼質(zhì)量更高,可以減少本地代碼的執(zhí)行時(shí)間,從而抵消額外的編譯時(shí)間開(kāi)銷(xiāo),因此很多非服務(wù)端的虛擬機(jī)選擇-server模式來(lái)運(yùn)行。

2.2、編譯對(duì)象與觸發(fā)條件

在上文我們有提到,JIT 編譯器會(huì)將熱點(diǎn)代碼編譯成本地平臺(tái)相關(guān)的機(jī)器碼。

哪些代碼會(huì)被 JIT 編譯器判斷為“熱點(diǎn)代碼”呢?主要有兩類:

  • 被多次調(diào)用的方法
  • 被多次執(zhí)行的循環(huán)體

這兩種情況都會(huì)使即時(shí)編譯器以整個(gè)方法作為編譯對(duì)象。

比較難以理解的可能是第二種情況,對(duì)于被多次執(zhí)行的循環(huán)體,可以理解成以一個(gè)方法可能只被調(diào)用一次或者少量的幾次,但是方法體內(nèi)部存在循環(huán)次數(shù)較多的循環(huán)體問(wèn)題,這樣循環(huán)體的代碼也會(huì)被重復(fù)執(zhí)行多次,因此這些代碼也被認(rèn)為是“熱點(diǎn)代碼”。

上面提到的都是概念知識(shí),虛擬機(jī)如何判斷一段代碼是否是“熱點(diǎn)代碼”呢?主要有兩種辦法:

  • 基于采樣的熱點(diǎn)探測(cè)
  • 基于計(jì)數(shù)器的熱點(diǎn)探測(cè)

HotSpot 虛擬機(jī)中使用的是第二種基于計(jì)數(shù)器的熱點(diǎn)探測(cè)方法,它為每個(gè)方法準(zhǔn)備了兩類計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器。

在確認(rèn)虛擬機(jī)運(yùn)行參數(shù)的前提下,這兩類計(jì)數(shù)器都有一個(gè)確認(rèn)的的閥值,當(dāng)計(jì)數(shù)器超過(guò)閥值時(shí),就會(huì)觸發(fā)即時(shí)編譯器。

下面我們一起來(lái)看看這兩類計(jì)數(shù)器的實(shí)現(xiàn)。

2.2.1、方法調(diào)用計(jì)數(shù)器

方法調(diào)用計(jì)數(shù)器,通常用于統(tǒng)計(jì)方法被調(diào)用的次數(shù)。它的默認(rèn)閾值在Client模式下是 1500 次,在Server模式下是 10000 次,這個(gè)閾值可以通過(guò)-XX:CompileThreshold參數(shù)來(lái)人為設(shè)定。

當(dāng)一個(gè)方法被調(diào)用時(shí),會(huì)檢查方法是否存在被 JIT 編譯過(guò)的版本,如果存在,則優(yōu)先使用編譯后的本地機(jī)器碼來(lái)執(zhí)行;如果不存在,將此方法的調(diào)用計(jì)數(shù)器值加 1,然后判斷方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器之和是否超過(guò)方法調(diào)用計(jì)數(shù)器的閾值,如果超過(guò),向即時(shí)編譯器提交一個(gè)該方法的代碼編譯請(qǐng)求,在默認(rèn)不設(shè)置的情況下,不會(huì)同步等待編譯請(qǐng)求完成,而時(shí)直接以解釋方式執(zhí)行方法。

具體流程,可以用如下圖來(lái)概括。

圖片圖片

如果不設(shè)置閥值的情況下,方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是方法被調(diào)用的絕對(duì)次數(shù),而是一個(gè)相對(duì)的執(zhí)行頻率,即一段時(shí)間之內(nèi)方法被調(diào)用的次數(shù)。

當(dāng)超過(guò)一定的時(shí)間限制,如果方法的調(diào)用次數(shù)不足以讓它提交給即時(shí)編譯器編譯,那這個(gè)方法的調(diào)用計(jì)數(shù)器就會(huì)少一半,這個(gè)過(guò)程稱為方法的調(diào)用計(jì)數(shù)器熱度衰減,而這段時(shí)間就稱為此方法統(tǒng)計(jì)的半衰周期。

進(jìn)行熱度衰減的動(dòng)作是在虛擬機(jī)進(jìn)行垃圾回收時(shí)順便進(jìn)行的,可以通過(guò)-XX:-UseCounterDecay參數(shù)來(lái)關(guān)閉熱度衰減,讓方法計(jì)數(shù)器統(tǒng)計(jì)方法調(diào)用的絕對(duì)次數(shù),這樣一來(lái),只要系統(tǒng)運(yùn)行時(shí)間足夠長(zhǎng),基本上絕大部分方法都會(huì)被編譯成本地機(jī)器碼。

此外,用戶也可以通過(guò)-XX:CounterHalfLifeTime參數(shù)來(lái)設(shè)置半衰周期的時(shí)間,單位是秒。

2.2.2、回邊計(jì)數(shù)器

回邊計(jì)數(shù)器,通常用于統(tǒng)計(jì)一個(gè)方法中循環(huán)體代碼執(zhí)行的次數(shù)。在字節(jié)碼方法循環(huán)體中,遇到控制流向后跳轉(zhuǎn)的指令成為"回邊",這個(gè)過(guò)程會(huì)產(chǎn)生“棧上替換”的行為,也就是方法棧幀還在棧上,只是方法被替換了,HotSpot 把這個(gè)過(guò)程觸發(fā)的即時(shí)編譯,稱之為 OSR 編譯。

關(guān)于回邊計(jì)數(shù)器的閾值設(shè)置,虛擬機(jī)沒(méi)有明確給出對(duì)應(yīng)的參數(shù),但是可以通過(guò)-XX:OnStackReplacePercentage參數(shù)來(lái)間接的調(diào)整回邊計(jì)數(shù)器的閾值,這個(gè)參數(shù)也稱為 ORS 比率,回邊計(jì)數(shù)器的閾值計(jì)算公式如下:

  • Client 模式:方法調(diào)用計(jì)數(shù)器閾值 × OSR 比率 / 1000,其中 OSR 比率默認(rèn)值933,如果都取默認(rèn)值,回邊計(jì)數(shù)器的閾值應(yīng)該是 13995
  • Server 模式:方法調(diào)用計(jì)數(shù)器閾值 × ( OSR 比率 - 解釋器監(jiān)控比率) / 100,其中 OSR 比率默認(rèn) 140,解釋器監(jiān)控比率默認(rèn)33,如果都取默認(rèn)值,回邊計(jì)數(shù)器閾值應(yīng)該是 10700

當(dāng)解釋器遇到一條回邊指令時(shí),會(huì)先查找需要執(zhí)行的代碼片段中是否有已經(jīng)編譯的版本,如果有,會(huì)優(yōu)先執(zhí)行已編譯好的代碼;如果沒(méi)有,就會(huì)把回邊計(jì)數(shù)器的值加 1,然后判斷方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器值之和是否超過(guò)回邊計(jì)數(shù)器的閾值,如果超過(guò),就會(huì)向即時(shí)編譯器提交一個(gè) OSR 編譯請(qǐng)求,并且把回邊計(jì)數(shù)器的值降低一些,以便繼續(xù)在解釋器中執(zhí)行循環(huán)。

具體流程,可以用如下圖來(lái)概括。

圖片圖片

與方法計(jì)數(shù)器不同,回邊計(jì)數(shù)器沒(méi)有熱度衰減的過(guò)程,因此這個(gè)計(jì)數(shù)器統(tǒng)計(jì)的就是該方法循環(huán)執(zhí)行的絕對(duì)次數(shù),當(dāng)回邊計(jì)數(shù)器溢出的時(shí)候,虛擬機(jī)還會(huì)把方法計(jì)數(shù)器的值也調(diào)整成溢出狀態(tài),這樣下次再進(jìn)入該方法的時(shí)候,就會(huì)執(zhí)行標(biāo)準(zhǔn)的編譯過(guò)程。

三、運(yùn)行期優(yōu)化技術(shù)

HotSpot 虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)為了實(shí)現(xiàn)程序更快的執(zhí)行效率,列出了很多的優(yōu)化手段,比如方法內(nèi)聯(lián)、冗余訪問(wèn)消除、復(fù)寫(xiě)傳播、無(wú)用代碼消除、公共子表達(dá)式消除、數(shù)組邊界檢查消除、逃逸分析等。

下面我們抽取幾個(gè)最常見(jiàn)的優(yōu)化技術(shù),一起來(lái)看看相關(guān)的實(shí)現(xiàn)。

3.1、公共子表達(dá)式消除

公共子表達(dá)式消除是一個(gè)普遍應(yīng)用于各種編譯器的經(jīng)典優(yōu)化技術(shù)。

如果一個(gè)子表達(dá)式已經(jīng)計(jì)算過(guò)了,且表達(dá)式中變量的值不曾發(fā)生變化,那么這個(gè)子表達(dá)式就可以當(dāng)做公共子表達(dá)式。

對(duì)于這種表達(dá)式,沒(méi)有必要再花時(shí)間去對(duì)它進(jìn)行計(jì)算,直接用前面計(jì)算過(guò)的表達(dá)式結(jié)果替代就可以了。如果這種優(yōu)化僅限于程序的基本塊內(nèi),便稱為局部公共子表達(dá)式消除;如果這種優(yōu)化的范圍涵蓋了多個(gè)基本塊,便稱為全局公共子表達(dá)式消除。

舉個(gè)簡(jiǎn)單的例子,假設(shè)存在以下代碼。

// 原始代碼
int d = (c * b) * 12 + a + (a + b * c);

如果這段代碼交給 Javac 編譯器則不會(huì)進(jìn)行任何優(yōu)化,但是這段代碼進(jìn)入到虛擬機(jī)即時(shí)編譯器之后,它將會(huì)進(jìn)行如下優(yōu)化。

// 將 c*b 和 b*c 用 E 表示,消除公共子表達(dá)式
int d = E * 12 + a + (a + E);

即時(shí)編譯器還可能進(jìn)行另一種叫做代數(shù)簡(jiǎn)化的優(yōu)化,把表達(dá)式變?yōu)椋?/p>

// 代數(shù)簡(jiǎn)化
int d = E * 13 + a + a;

表達(dá)式變換之后,再次計(jì)算可以節(jié)省一些時(shí)間。

3.2、數(shù)組邊界檢查消除

數(shù)組邊界檢查消除也是一個(gè)經(jīng)典優(yōu)化技術(shù)。

Java 語(yǔ)言作為一門(mén)動(dòng)態(tài)安全的語(yǔ)言,會(huì)自動(dòng)對(duì)數(shù)組的讀寫(xiě)訪問(wèn)索引合法性做檢查,當(dāng)超出地址范圍,會(huì)拋出java.lang.ArrayIndexOutOfBoundsException異常,這對(duì)軟件開(kāi)發(fā)很友好,但對(duì) JVM 卻是一個(gè)性能負(fù)擔(dān)。

如果能在編譯期根據(jù)數(shù)據(jù)流分析判定索引一直在數(shù)組邊界內(nèi),就可以消除數(shù)組上下邊界的檢測(cè),從而節(jié)省很多次條件判斷操作。

類似的消除手段還有空指針檢查(NullPointException)、除數(shù)為零檢查(ArithmeticException)、自動(dòng)裝箱消除(Autobox Elimination)、安全點(diǎn)消除(Safepoint Elimination)、消除反射(Dereflection)等等,針對(duì)這些檢查的消除方式,可能會(huì)采用隱式異常處理的思路。

舉個(gè)簡(jiǎn)單的例子,假設(shè)存在以下代碼。

// 原始偽代碼
if(foo != null) {
    return foo.value;
} else {
    throw new NullPointException();
}

優(yōu)化后的偽代碼。

// 隱式異常消除后的偽代碼
try {
    return foo.value;
} catch (segment_fault) {
    uncommon_trap();
}

JVM 會(huì)注冊(cè)一個(gè) Segment Fault 信號(hào)的異常處理器(uncommon_trap() 是一個(gè)針對(duì)進(jìn)程層面的異常處理器,與 try-catch 的線程級(jí)異常處理器不同),當(dāng) foo 不為空,可以省去判空的開(kāi)銷(xiāo);如果 foo 真為空,會(huì)轉(zhuǎn)到異常處理器恢復(fù)中斷并拋出NullPointException異常。

借助 JVM 在運(yùn)行期收集的性能監(jiān)控信息,判定 foo 極少為空時(shí),采用這樣的優(yōu)化方式可以提升程序的執(zhí)行效率。

3.3、方法內(nèi)聯(lián)

方法內(nèi)聯(lián)是 JVM 最重要的優(yōu)化手段之一,它可以去除方法調(diào)用的成本(如減少建立棧幀等),為其它優(yōu)化建立了良好的基礎(chǔ)。

虛擬機(jī)如果探測(cè)到某個(gè)方法是熱點(diǎn)方法并且長(zhǎng)度不太長(zhǎng)時(shí),會(huì)進(jìn)行內(nèi)聯(lián),所謂的內(nèi)聯(lián)就是把方法內(nèi)代碼拷貝、粘貼到調(diào)用者的位置。

舉個(gè)例子!

public final static void method1(){
    handleA();
    handleB();
}
public static void main(String[] args) {
    handle1();
    method1();
    handle2();
}

優(yōu)化之后可能變成:

public static void main(String[] args) {
    handle1();
    handleA();
    handleB();
    handle2();
}

從效果上看,就是把method1()方法拷貝到main()方法中。未拷貝之前,優(yōu)化空間非常小,但是合并到一個(gè)方法之后,就有了很大的優(yōu)化空間了。

再比如下面這個(gè)例子!

private final  static int method1(final int i) {
    return i * i;
}
public static void main(String[] args) {
    System.out.println(method1(10));
}

優(yōu)化之后可能變成:

public static void main(String[] args) {
    System.out.println(100);
}

虛擬機(jī)還能夠進(jìn)行常量折疊(constant folding)的優(yōu)化,減少不必要的代碼執(zhí)行環(huán)節(jié),從而提升代碼執(zhí)行效率。

再次想到一個(gè)問(wèn)題,在 Java 編程規(guī)范里面,可能很多新人不能理解為什么推薦盡量將方法聲明為final?

我們知道 Java 是多態(tài)的特性,子類既可以調(diào)用父類方法,也可以重寫(xiě)父類方法,編程方面靈活性非常高,這樣其實(shí)會(huì)導(dǎo)致一個(gè)問(wèn)題,編譯期間無(wú)法確定應(yīng)該使用哪一個(gè)方法,只有在運(yùn)行時(shí)才能確定,這就可能導(dǎo)致虛擬機(jī)很難對(duì)方法進(jìn)行內(nèi)聯(lián)操作。

但是如果將方法聲明為final,這些方法是無(wú)法被重寫(xiě),方法 A 調(diào)用方法 B 基本上是可以完全確定的,可以進(jìn)行方法內(nèi)聯(lián)操作。

3.4、逃逸分析

逃逸分析,在之前對(duì)象內(nèi)存分配的文章中有所簡(jiǎn)單的介紹過(guò),我們?cè)俅位仡櫼幌孪嚓P(guān)的知識(shí)。

逃逸分析是一項(xiàng)比較前沿的優(yōu)化技術(shù),它并不是直接優(yōu)化代碼的手段,而是為其它優(yōu)化手段提供了分析技術(shù)。

逃逸分析的基本行為是分析對(duì)象動(dòng)態(tài)作用域。

當(dāng)一個(gè)對(duì)象在方法里面被定義后,它可能被外部方法所引用,例如作為調(diào)用參數(shù)傳遞到其他方法中,這種稱為方法逃逸;甚至還有可能被外部線程訪問(wèn)到,譬如賦值給可以在其他線程中訪問(wèn)的實(shí)例變量,這種稱為線程逃逸。

如果能證明一個(gè)對(duì)象不會(huì)逃移到方法外或者線程之外,換句話說(shuō)就是別的方法或線程無(wú)法通過(guò)任何途徑訪問(wèn)到這個(gè)對(duì)象,則可以通過(guò)一些途徑為這個(gè)變量進(jìn)行一些不同程度的優(yōu)化。

3.4.1、棧上分配

在之前的對(duì)象創(chuàng)建文章中,我們提及過(guò),對(duì)象會(huì)優(yōu)先在堆上分配,垃圾收集器會(huì)定期回收堆空間中不再使用的對(duì)象,但這塊的內(nèi)存回收很耗時(shí)。如果確定一個(gè)對(duì)象不會(huì)逃逸出方法之外,讓這個(gè)對(duì)象在棧上分配,對(duì)象所占用的內(nèi)存空間就可以隨著棧幀出棧而銷(xiāo)毀,這樣垃圾收集器的壓力將會(huì)小很多。

3.4.2、同步消除

線程同步本身是一個(gè)相對(duì)耗時(shí)的過(guò)程,如果逃逸分析能夠確定一個(gè)變量不會(huì)逃逸出線程,無(wú)法被其他線程訪問(wèn),那么這個(gè)變量的讀寫(xiě)肯定就不會(huì)有競(jìng)爭(zhēng),此時(shí)虛擬機(jī)會(huì)對(duì)這個(gè)變量,實(shí)施的同步措施進(jìn)行消除。

比如你定義的類的方法上有同步鎖,但在運(yùn)行時(shí),卻只有一個(gè)線程在訪問(wèn),此時(shí)逃逸分析后,虛擬機(jī)會(huì)去掉同步鎖來(lái)運(yùn)行。

3.4.3、標(biāo)量替換

標(biāo)量是指一個(gè)數(shù)據(jù)已經(jīng)無(wú)法再分解成更小的數(shù)據(jù)來(lái)表示了,比如 Java 虛擬機(jī)中的原始數(shù)據(jù)類型(int,long 等數(shù)值類型以及 reference 類型)等都不能進(jìn)一步分解,它們可以稱為標(biāo)量。相對(duì)的,如果一個(gè)數(shù)據(jù)可以繼續(xù)分解,那它稱為聚合量。

Java 中最典型的聚合量是對(duì)象,如果逃逸分析證明一個(gè)對(duì)象不會(huì)被外部訪問(wèn),并且這個(gè)對(duì)象是可分解的,那程序真正執(zhí)行的時(shí)候?qū)⒖赡懿粍?chuàng)建這個(gè)對(duì)象,而改為直接創(chuàng)建它的若干個(gè)被這個(gè)方法使用到的成員變量來(lái)代替,拆散后的變量便可以被單獨(dú)分析與優(yōu)化,可以各自分別在棧幀或寄存器上分配空間,原本的對(duì)象就無(wú)需整體分配空間了。

關(guān)于逃逸分析的論文早在 1999 年就已經(jīng)發(fā)表,但直到 Sun JDK1.6 才實(shí)現(xiàn)了逃逸分析,直到現(xiàn)在這項(xiàng)優(yōu)化尚未足夠成熟,仍有很大改進(jìn)余地。

不成熟的原因主要是不能保證逃逸分析的性能收益必定能高于它的消耗。雖然在實(shí)際測(cè)試結(jié)果中,實(shí)施逃逸分析后的程序往往能運(yùn)行出不錯(cuò)的成績(jī),但是在實(shí)際的應(yīng)用程序,尤其是大型程序中反而發(fā)現(xiàn)實(shí)施逃逸分析可能出現(xiàn)效果不穩(wěn)定的情況,或因分析過(guò)程耗時(shí)但卻無(wú)法有效判別出非逃逸對(duì)象而導(dǎo)致性能有所下降。

如果有需要并且確認(rèn)對(duì)程序運(yùn)行有益,用戶可以使用-XX:+DoEscapeAnalysis參數(shù)來(lái)手動(dòng)開(kāi)啟逃逸分析,開(kāi)啟之后可以通過(guò)-XX:+PrintEscapeAnalysis參數(shù)來(lái)查看分析結(jié)果,用戶還可以使用-XX:+EliminateAllocations參數(shù)來(lái)開(kāi)啟標(biāo)量替換,使用-XX:+EliminatLocks參數(shù)來(lái)開(kāi)啟同步消除,使用-XX:+PrintEliminateAllocations參數(shù)查看標(biāo)量的替換情況。

四、小結(jié)

本文主要圍繞 JVM 在運(yùn)行期對(duì)代碼采取的一些優(yōu)化手段,進(jìn)行了一次知識(shí)內(nèi)容整合和總結(jié),希望能幫助到大家。

內(nèi)容比較多,如果有描述不對(duì)的地方,歡迎留言指出,不勝感激。

五、參考

1.https://www.cnblogs.com/xrq730/p/4857820.html

2.https://juejin.cn/post/7236634386568527928

3.https://blog.csdn.net/ChaoMing_H/article/details/129179684

責(zé)任編輯:武曉燕 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2024-03-07 17:21:12

HotSpotJVMHot Code

2011-06-01 14:18:41

JVM

2018-06-29 13:24:48

沙箱容器解決方案

2023-11-11 19:07:23

JVMJava

2024-04-17 12:58:15

MySQL索引數(shù)據(jù)庫(kù)

2010-09-17 17:10:30

MS JVM

2010-09-27 09:23:42

JVM調(diào)優(yōu)

2011-06-20 10:36:29

SEO

2024-04-24 10:24:09

2024-01-29 08:24:40

2013-11-25 14:57:04

TCPTCP優(yōu)化

2009-07-10 14:55:34

2011-12-16 13:45:22

2009-12-10 10:32:43

2011-05-26 13:26:42

if

2009-07-09 16:33:06

eclipse jvm

2009-07-09 17:36:58

jvm.cfg

2009-05-04 09:52:49

Oracle優(yōu)化排序

2009-07-21 17:41:58

JDBC數(shù)據(jù)源

2012-03-22 09:31:14

Java
點(diǎn)贊
收藏

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