日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Java對(duì)象竟然會(huì)在棧上分配內(nèi)存?

 1 逃逸分析

創(chuàng)新互聯(lián)專(zhuān)注于道縣網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供道縣營(yíng)銷(xiāo)型網(wǎng)站建設(shè),道縣網(wǎng)站制作、道縣網(wǎng)頁(yè)設(shè)計(jì)、道縣網(wǎng)站官網(wǎng)定制、微信小程序開(kāi)發(fā)服務(wù),打造道縣網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供道縣網(wǎng)站排名全網(wǎng)營(yíng)銷(xiāo)落地服務(wù)。

JVM中高深的優(yōu)化技術(shù),如同類(lèi)繼承關(guān)系分析,該技術(shù)并非直接去優(yōu)化代碼,而是一種為其他優(yōu)化措施提供依據(jù)的分析技術(shù)。

分析對(duì)象的動(dòng)態(tài)作用域,當(dāng)某對(duì)象在方法里被定義后,它可能

  • 方法逃逸

被外部方法引用,例如作為參數(shù)傳遞給其他方法

  • 線程逃逸

被外部線程訪問(wèn),例如賦值給可以在其他線程中訪問(wèn)的實(shí)例變量

所以 Java 對(duì)象由低到高的逃逸程度即為:

  • 不逃逸 =》
  • 方法逃逸 =》
  • 線程逃逸

若能確定一個(gè)對(duì)象

  • 不會(huì)逃逸到方法或線程外(即其它方法、線程無(wú)法訪問(wèn)到該對(duì)象)
  • 或逃逸程度較低(只逃逸出方法而不逃逸出線程)

則可為該對(duì)象實(shí)例采取不同程度的優(yōu)化方案。

2 優(yōu)化方案

2.1 棧上分配(Stack Allocations)

由于復(fù)雜度等原因,HotSpot中目前暫時(shí)還沒(méi)有做這項(xiàng)優(yōu)化,但一些其他的虛擬機(jī)(如Excelsior JET)使用了該優(yōu)化。

JVM的GC模塊會(huì)回收堆中不再使用的對(duì)象,但如下回收動(dòng)作

  • 標(biāo)記篩選出可回收對(duì)象
  • 回收和整理內(nèi)存

都需耗費(fèi)大量資源。

若確定一個(gè)對(duì)象不會(huì)逃逸出線程,那讓該對(duì)象在棧上分配內(nèi)存就是個(gè)不錯(cuò)主意,對(duì)象所占用內(nèi)存空間就可隨棧幀出棧而銷(xiāo)毀。

在一般應(yīng)用中,完全不會(huì)逃逸的局部對(duì)象和不會(huì)逃逸出線程的對(duì)象所占比例很大,若能使用棧上分配,則大量對(duì)象就會(huì)隨方法結(jié)束而自動(dòng)銷(xiāo)毀,GC系統(tǒng)壓力會(huì)下降很多。

棧上分配可支持方法逃逸,但不能支持線程逃逸。

2.2 標(biāo)量替換(Scalar Replacement)

2.2.1 標(biāo)量

若一個(gè)數(shù)據(jù)已經(jīng)無(wú)法再分解成更小數(shù)據(jù),JVM中的原始數(shù)據(jù)類(lèi)型(如 int、long 等數(shù)值類(lèi)型及 reference 類(lèi)型)都不能再進(jìn)一步分解,這些數(shù)據(jù)即為標(biāo)量。

2.2.2 聚合量

若一個(gè)數(shù)據(jù)可繼續(xù)分解,則稱(chēng)為聚合量(Aggregate),比如 Java 對(duì)象就是聚合量。

2.2.3 標(biāo)量替換

把一個(gè)Java對(duì)象拆散,根據(jù)程序訪問(wèn)情況,將其用到的成員變量恢復(fù)為原始類(lèi)型來(lái)訪問(wèn)。

假如逃逸分析能證明一個(gè)對(duì)象不會(huì)被方法外部訪問(wèn),并且該對(duì)象可被分解,那么程序真正執(zhí)行時(shí)將可能不去創(chuàng)建該對(duì)象,而改為直接創(chuàng)建它的若干個(gè)被這方法使用的成員變量。

將對(duì)象拆分后:

  • 可讓對(duì)象的成員變量在棧上 (棧上存儲(chǔ)的數(shù)據(jù),很大概率會(huì)被JVM分配至物理機(jī)器的高速寄存器中存儲(chǔ))分配和讀寫(xiě)
  • 為后續(xù)進(jìn)步優(yōu)化創(chuàng)建條件

2.2.4 適用場(chǎng)景

標(biāo)量替換可視為棧上分配一種特例,實(shí)現(xiàn)更簡(jiǎn)單(不用考慮對(duì)象完整結(jié)構(gòu)的分配),但對(duì)逃逸程度的要求更高,它不允許對(duì)象逃逸出方法范圍內(nèi)。

2.3 同步消除(Synchronization Elimination)

線程同步是個(gè)相對(duì)耗時(shí)的過(guò)程,若逃逸分析能確定一個(gè)變量不會(huì)逃逸出線程,即不會(huì)被其他線程訪問(wèn),則該變量的讀寫(xiě)肯定不會(huì)有線程競(jìng)爭(zhēng), 也可安全消除對(duì)該變量實(shí)施的同步措施。

逃逸分析的論文在1999年就已發(fā)表,但到JDK 6,HotSpot才開(kāi)始初步支持逃逸分析,至今該也尚未成熟,主要因?yàn)樘右莘治龅挠?jì)算成本高到無(wú)法保證帶來(lái)的性能收益會(huì)高于它的消耗。要百分百準(zhǔn)確判斷一個(gè)對(duì)象是否會(huì)逃逸,需進(jìn)行一系列復(fù)雜數(shù)據(jù)流敏感的過(guò)程間分析,才能確定程序各個(gè)分支執(zhí)行時(shí)對(duì)此對(duì)象的影響。過(guò)程間分析這種大壓力的分析算法正是即時(shí)編譯的弱項(xiàng)。試想,若逃逸分析完畢后發(fā)現(xiàn)幾乎找不到幾個(gè)不逃逸的對(duì)象, 那這些運(yùn)行期耗用的時(shí)間就白費(fèi)了,所以目前JVM只能采用不那么準(zhǔn)確,但時(shí)間壓力相對(duì)較小的算法來(lái)完成分析。

C和C++原生支持棧上分配(不使用new即可),靈活運(yùn)用棧內(nèi)存方面,Java的確是弱勢(shì)群體。

在現(xiàn)在仍處于實(shí)驗(yàn)階段的Valhalla項(xiàng)目,設(shè)計(jì)了新的inline關(guān)鍵字用于定義Java的內(nèi)聯(lián)類(lèi)型, 對(duì)標(biāo)C#的值類(lèi)型。有了該標(biāo)識(shí)與約束,以后逃逸分析做起來(lái)就會(huì)簡(jiǎn)單很多。

3 代碼實(shí)戰(zhàn)驗(yàn)證

3.1 全無(wú)優(yōu)化的代碼

 
 
 
 
  1. public int test(int x) {  
  2.   int xx = x + 2;  
  3.   Point p = new Point(xx, 42);  
  4.   return p.getX();  

3.2 優(yōu)化step1:內(nèi)聯(lián)構(gòu)造器和getX()方法

 
 
 
 
  1. public int test(int x) {  
  2.   int xx = x + 2; 
  3.   // 在堆中分配P對(duì)象  
  4.   Point p = point_memory_alloc(); 
  5.   // Point構(gòu)造器被內(nèi)聯(lián)后   
  6.   p.x = xx;  
  7.   p.y = 42; 
  8.   // Point::getX()被內(nèi)聯(lián)后  
  9.   return p.x; 

優(yōu)化step2:標(biāo)量替換

逃逸分析后,發(fā)現(xiàn)在整個(gè)test()方法的范圍內(nèi)Point對(duì)象實(shí)例不會(huì)發(fā)生任何程度逃逸, 便可對(duì)它進(jìn)行標(biāo)量替換:把其內(nèi)部的x和y直接置換出來(lái),分解為test()方法內(nèi)的局部變量,從而避免了Point對(duì)象實(shí)例的創(chuàng)建

 
 
 
 
  1. public int test(int x) {  
  2.    int xx = x + 2;  
  3.    int px = xx;  
  4.    int py = 42  
  5.    return px;  

step3:無(wú)效代碼消除

數(shù)據(jù)流分析,發(fā)現(xiàn)py的值其實(shí)對(duì)方法不會(huì)造成任何影響,那就可以放心地去做無(wú)效代碼消除得到最終優(yōu)化結(jié)果,如下所示:

 
 
 
 
  1. public int test(int x) {  
  2.   return x + 2;  

觀察測(cè)試結(jié)果,實(shí)施逃逸分析后的程序在MicroBenchmarks中往往能得到不錯(cuò)的成績(jī),但在實(shí)際應(yīng)用程序中,尤其是大型程序中反而發(fā)現(xiàn)實(shí)施逃逸分析可能出現(xiàn)效果不穩(wěn)定,或分析過(guò)程耗時(shí)但卻無(wú)法有效判別出非逃逸對(duì)象而導(dǎo)致性能(即時(shí)編譯的收益)下降,所以曾經(jīng)在很長(zhǎng)的一段時(shí)間,即使是服務(wù)端編譯器,也默認(rèn)不開(kāi)啟逃逸分析(從JDK 6 Update 23開(kāi)始,服務(wù)端編譯器中開(kāi)始才默認(rèn)開(kāi)啟逃逸分析。),甚至在某些版本(如JDK 6 Update 18)中還曾完全禁止這項(xiàng)優(yōu)化,一直到JDK 7時(shí)這項(xiàng)優(yōu)化才成為服務(wù)端編譯器默認(rèn)開(kāi)啟的選項(xiàng)。

若有需要或確認(rèn)對(duì)程序有益,可使用參數(shù):

  • -XX:+DoEscapeAnalysis 手動(dòng)開(kāi)啟逃逸分析

開(kāi)啟后可通過(guò)參數(shù):

  • -XX:+PrintEscapeAnalysis 查看分析結(jié)果

有逃逸分析支持后,用戶可使用如下參數(shù):

  • -XX:+EliminateAllocations 開(kāi)啟標(biāo)量替換
  • +XX:+EliminateLocks 開(kāi)啟同步消除
  • -XX:+PrintEliminateAllocations 查看標(biāo)量的替換情況

讓我們一起期待該JIT優(yōu)化技術(shù)之逃逸分析的發(fā)展。

參考

《深入理解 Java 虛擬機(jī)》

本文轉(zhuǎn)載自微信公眾號(hào)「JavaEdge」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系JavaEdge公眾號(hào)。


文章標(biāo)題:Java對(duì)象竟然會(huì)在棧上分配內(nèi)存?
分享路徑:http://www.dlmjj.cn/article/dhiijdj.html