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

Java對(duì)象竟然會(huì)在棧上分配內(nèi)存?

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

[[388957]]

 1 逃逸分析

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

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

  • 方法逃逸

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

  • 線程逃逸

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

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

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

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

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

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

2 優(yōu)化方案

2.1 棧上分配(Stack Allocations)

由于復(fù)雜度等原因,HotSpot中目前暫時(shí)還沒有做這項(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)存空間就可隨棧幀出棧而銷毀。

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

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

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

2.2.1 標(biāo)量

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

2.2.2 聚合量

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

2.2.3 標(biāo)量替換

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

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

將對(duì)象拆分后:

  • 可讓對(duì)象的成員變量在棧上 (棧上存儲(chǔ)的數(shù)據(jù),很大概率會(huì)被JVM分配至物理機(jī)器的高速寄存器中存儲(chǔ))分配和讀寫
  • 為后續(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í)的過程,若逃逸分析能確定一個(gè)變量不會(huì)逃逸出線程,即不會(huì)被其他線程訪問,則該變量的讀寫肯定不會(huì)有線程競(jìng)爭(zhēng), 也可安全消除對(duì)該變量實(shí)施的同步措施。

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

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)類型, 對(duì)標(biāo)C#的值類型。有了該標(biāo)識(shí)與約束,以后逃逸分析做起來就會(huì)簡(jiǎn)單很多。

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

3.1 全無優(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直接置換出來,分解為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:無效代碼消除

數(shù)據(jù)流分析,發(fā)現(xiàn)py的值其實(shí)對(duì)方法不會(huì)造成任何影響,那就可以放心地去做無效代碼消除得到最終優(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)定,或分析過程耗時(shí)但卻無法有效判別出非逃逸對(duì)象而導(dǎo)致性能(即時(shí)編譯的收益)下降,所以曾經(jīng)在很長(zhǎng)的一段時(shí)間,即使是服務(wù)端編譯器,也默認(rèn)不開啟逃逸分析(從JDK 6 Update 23開始,服務(wù)端編譯器中開始才默認(rèn)開啟逃逸分析。),甚至在某些版本(如JDK 6 Update 18)中還曾完全禁止這項(xiàng)優(yōu)化,一直到JDK 7時(shí)這項(xiàng)優(yōu)化才成為服務(wù)端編譯器默認(rèn)開啟的選項(xiàng)。

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

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

開啟后可通過參數(shù):

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

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

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

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

參考

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

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

 

責(zé)任編輯:武曉燕 來源: JavaEdge
相關(guān)推薦

2022-12-12 08:42:06

Java對(duì)象棧內(nèi)存

2009-06-03 15:52:34

堆內(nèi)存棧內(nèi)存Java內(nèi)存分配

2021-05-12 07:03:25

Switch報(bào)空指針

2015-11-16 11:22:05

Java對(duì)象內(nèi)存分配

2024-04-29 09:38:16

2022-03-16 08:39:19

StackHeap內(nèi)存

2018-02-08 14:57:22

對(duì)象內(nèi)存分配

2021-09-28 07:12:09

函數(shù)內(nèi)存

2018-04-08 08:45:53

對(duì)象內(nèi)存策略

2013-11-07 09:42:42

對(duì)象對(duì)象池加速

2022-10-08 08:01:07

JVMTLABPLAB

2010-09-25 14:12:50

Java內(nèi)存分配

2021-02-28 13:22:54

Java內(nèi)存代碼

2020-11-23 07:08:17

JVM逃逸元空間

2010-09-17 16:14:22

Java內(nèi)存分配

2020-05-27 21:13:27

JavaJVM內(nèi)存

2020-09-29 06:45:49

JDK

2011-07-11 18:02:50

java

2011-07-11 18:10:28

java

2010-09-25 14:38:18

Java內(nèi)存分配
點(diǎn)贊
收藏

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