中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

怎么使用java Hotspot虛擬機內的即時編譯器

發布時間:2021-11-16 14:26:03 來源:億速云 閱讀:179 作者:iii 欄目:大數據

這篇文章主要介紹“怎么使用java Hotspot虛擬機內的即時編譯器”,在日常操作中,相信很多人在怎么使用java Hotspot虛擬機內的即時編譯器問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么使用java Hotspot虛擬機內的即時編譯器”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

在部分商用虛擬機(SunHotspot、IBMJ9)中,Java程序最初是通過解釋器解釋執行的,當虛擬機發現有個方法或代碼塊運行特別頻繁時,就會把這些代碼認定為“熱點代碼”。為了提高熱點代碼的執行效率,虛擬機會把這些代碼編譯成與本地平臺相關的機器碼,并進行各種層次的優化,完成這個任務的編譯器被稱為即時編譯器(JustInTimeCompiler,簡稱JIT編譯器)。即時編譯器并不是虛擬機必須的部分,但是即時編譯器編譯性能的好壞、代碼優化程度的高低卻是衡量一款商用虛擬機好壞與否最關鍵的指標之一,是虛擬機中最核心且最能體現虛擬機技術水平的部分。

Hotspot虛擬機內的即時編譯器

重點我們需要關注解決以下幾個問題:

為何Hotspot虛擬機要使用解釋器和編譯器并存的架構?
為何Hotspot虛擬機要實現兩個不同的即時編譯器?
程序何時使用解釋器執行?何時使用編譯器執行?
哪些程序代碼會被編譯為本地代碼?如何編譯為本地代碼?
如何從外部觀察即時編譯器的編譯過程和編譯結果?

解釋器與編譯器

盡管不是所有的Java虛擬機都采樣解釋器與編譯器并存的架構,但是許多主流的虛擬機,比如SunHotspot、IBMJ9,都同時包含解釋器與編譯器。解釋器與編譯器有各自的優勢:當程序需要快速啟動時,解釋器可以發揮作用,省去編譯時間立即執行在程序運行后,隨著時間的推移,編譯器逐漸發揮作用把越來越多的代碼編譯成本地代碼后,可以獲取更高的執行效率。
當程序運行環境的內存資源限制較大時,使用解釋器執行節省內存,反之可以使用編譯執行提升效率。同時,解釋器還可以作為編譯器激進優化時的一個“逃生門”,讓編譯器根據概率選擇一個大多數時候都能提升運行速度的優化手段,當激進優化的假設不成立時,比如加載了新類后類型繼承結構出現變化,出現“罕見陷阱”時可以通過逆優化退回到解釋狀態繼續執行。因此,在虛擬機中解釋器和編譯器經常配合工作,如下圖所示:

                                    怎么使用java Hotspot虛擬機內的即時編譯器

                                                         圖-解釋器與編譯器的交互

HotSpot虛擬機內置了兩個即時編譯器:ClientCompiler和ServerCompiler,簡稱C1編譯器和C2編譯器。默認采用解釋器和其中一個編譯器直接配合的方式工作,具體使用哪個編譯器,取決于虛擬機工作的模式,用戶可以使用-client參數或-server參數指定虛擬機的工作模式,還可以使用-Xint強制虛擬機運行于“解釋模式”。
由于即時編譯器編譯本地代碼需要占用程序運行時間,要編譯出優化程度更高的代碼,所需時間會更長。同時,解釋器還要替編譯器收集性能監控信息,這對解釋執行速度也有影響。為了在程序啟動響應速度與運行效率之間達到最佳平衡,HotSpot虛擬機又引入了分層編譯的策略。分層編譯根據編譯器編譯、優化的規模與耗時,劃分為不同的編譯層次,包括:
第0層,程序解釋執行,不開啟性能監控功能,可觸發第1層編譯。
第1層,稱為C1編譯,將字節碼編譯為本地代碼,并進行簡單可靠的優化,如有必要將加入性能監控邏輯。
第2層,稱為C2編譯,也是將字節碼編譯為本地代碼,但是會進行耗時較長的優化,甚至會根據性能監控信息進行一些不完全可靠的激進優化。
實施分層編譯后,ClientCompiler和ServerCompiler會同時工作,許多代碼可能會被編譯多次,用ClientCompiler獲得更快的編譯速度,用ServerCompiler獲取更好的編譯質量,在解釋執行的時候也無需再承擔收集性能監控信息的任務。

編譯對象與觸發條件

在運行過程中,會被即時編譯器編譯的熱點代碼有兩類:
被多次調用的方法。
被多次執行的循環體。
這兩種情況,編譯器都會編譯整個方法。因為編譯發生在方法執行過程中,因此形象地稱之為棧上替換(OnStackReplacement,簡稱OSR,即方法棧幀還在棧上,方法就被替換了)。
判斷一段代碼是不是熱點代碼,是否需要觸發即時編譯,這樣的行為稱為熱點探測(HotSpotDetection),熱點探測方式主要有兩種:
基于采樣的熱點探測:采用這種方法的虛擬機會周期性地檢查各個線程的棧頂,如果某個方法經常出現在棧頂,那它就是熱點方法。其優點是簡單、高效,還可以獲取方法調用關系;缺點是不夠精確,容易受到線程阻塞或其他外接因素的影響。
基于計數的熱點探測:采用這種方法的虛擬機會為每個方法(甚至是代碼塊)建立計數器,統計方法執行次數,次數超過一定閾值就認為是熱點方法。這種方法實現起來麻煩,但是其統計結果相對來說更加精確和嚴謹。
在HotSpot虛擬機里使用的是第二種方法,因此它為每個方法準備了兩類計數器:方法調用計數器(InvocationCounter)回邊計數器(BackedgeCounter)。在確定虛擬機運行參數的情況下,這兩個計數器都有一定的閾值,超過閾值就會觸發JIT編譯。

編譯優化技術

Java虛擬機設計團隊幾乎對代碼的所有優化措施都集中在了即時編譯器中,因此一般來說,即時編譯器產生的本地代碼會比javac產生的字節碼更加優秀。下面,我們介紹一些HotSpot虛擬機即時編譯器生成代碼時采用的代碼優化技術。

公共子表達式消除

公共子表達式消除是一個普遍應用于各種編譯器的經典優化技術,它的原理是:如果一個表達式E已經計算過了,并且從先前計算到現在E中所有變量的值都沒有發生變化,那么E的這次計算就稱為公共子表達。對于這種表達式,就沒有必要再對其進行計算了,使用之前計算過的值即可。

數組邊界檢查消除

數組邊界檢查消除是即時編譯器中語言相關的經典優化技術。如果有一個數組foo[],Java語言在訪問數組元素foo[i]時,系統會自動進行上下界的范圍檢查,即i的取值范圍是0~foo.length-1,否則將拋出運行時異常java.lang.ArrayIndexOutOfBoundsException。這對開發者來說是好事情,即時程序員沒有專門編寫防御代碼,也可以避免大部分的溢出攻擊。但是,對于虛擬機的執行子系統來說,每次數組元素的讀寫操作都帶有一次隱含的條件判定操作,對于擁有大量數組訪問的系統,無疑是一種性能上的負擔。
編譯器會對代碼進行分析,如果確定某次數組訪問一定不會越界,就可以去掉數組的上下界檢查。比如在循環訪問數組時,編譯器只要通過數據流分析確定循環變量的取值范圍一定在[0,foo.length)之間,就可以在整個循環中把數組上下界檢查消除。
與數組邊界檢查消除類似的優化,還有隱式異常處理,Java中空指針檢查和除數為零檢查都采用了這種思路。

方法內聯

方法內聯看起來簡單,但實際中很多方法都無法直接進行內聯。原因是除了使用invokespecial指令調用的私有方法、實例構造器、父類方法以及使用invokestatic指令調用的靜態方法,還有部分final方法能夠在編譯時唯一確定執行的方法版本,其他都可能存在多于一個版本的方法接收者,需要在運行時才能確定,這一類方法稱為虛方法,由于Java語言提倡使用面向對象的編程方式進行編程而Java語言默認的實例方法就是虛方法,因此內聯與虛方法之間產生矛盾
對于一個虛方法,編譯期做內聯的時候根本無法確定應該使用哪個方法的版本,為了解決虛方法的內聯問題,Java虛擬機引入了一種稱為“類型繼承關系分析”(ClassHierarchyAnalysis,CHA)的技術,這是一種基于整個應用程序的類型分析技術,它用于確定在目前已加載的類中,某個接口是否有多于一種實現,某個類是否存在子類、子類是否為抽象類等信息。
非虛方法可以直接內聯,如果是虛方法且通過CHA分析得知某個方法只有一個版本那也可以進行內聯,不過這種內聯屬于“激進優化”,需要預留一個“逃生門”,稱為守護內聯。如果程序在執行過程中,虛擬機一直沒有加載到令這個類繼承關系發生變化的類,那這個內聯優化的代碼就可以一直使用下去。但如果加載了導致繼承關系發生變化的新類,那就需要拋棄已經編譯的代碼,返回到解釋狀態執行,或者重新進行編譯。
如果CHA查出來某方法有多個版本,則編譯器還會進行最后一次努力,使用內聯緩存InlineCache來完成方法內聯,這是一個建立在目標方法正常入口之前的緩存,其工作原理是:在未發生方法調用之前,內聯緩存為空,當第一次調用發生后,緩存記錄下方法接收者的版本信息。后續每次執行都檢查版本,如果以后進來的每次調用的方法接受者版本都是一樣的,那么這個內聯還可以一直使用下去,否則查找虛方法表進行方法分派

逃逸分析

逃逸分析是目前Java虛擬機中比較前沿的優化技術,它與類型繼承關系分析一樣,并不是直接優化代碼的技術,而是為其他優化手段提供依據的分析技術。逃逸分析的基本行為是分析對象動態作用域:當一個對象在方法里定義后,它可能被外部方法所引用,例如作為調用參數傳遞到其他方法中,稱為方法逃逸。甚至還有可能被外部線程訪問到,譬如賦值給類變量或其他線程中訪問的實例變量,稱為線程逃逸
如果能證明一個對象不會逃逸到方法或線程之外,就可以為這個變量進行一些高效優化:
棧上分配:Java一般是在堆上分配對象的,對象的回收依賴虛擬機的垃圾收集系統,垃圾收集系統回收和整理內存都需要耗費時間。如果一個對象不會逃逸出方法之外,那讓這個對象在棧上分配會是一個不錯的主意,對象所占用的內存空間可以隨著棧幀出棧而銷毀,減輕了垃圾收集系統的壓力。
同步消除:線程同步本身是一個相對耗時的過程,如果逃逸線程分析確定一個對象不會逃逸出線程,不會被其他線程訪問,那這個變量的讀寫肯定不會有競爭,對這個變量實施的同步措施也就可以消除掉。
標量替換:標量是指一個數據已經無法再分解成更小的數據了,Java中的原始數據類型都不能再進一步分解,就可以稱為標量。相對的,如果一個數據可以繼續分解,就可以稱作聚合量,Java中的對象就是典型的聚合量。如果把一個Java對象拆散,根據程序訪問情況,將其使用到的成員變量恢復原始類型來訪問就叫做標量替換。如果逃逸分析證明一個對象不會被外部訪問,并且這個對象可以被拆散的話,那程序真正執行的時候就可能不創建這個對象,改為直接創建它的若干個成員變量來代替。將對象拆分后,除了可以讓成員變量在棧上分配和讀寫外,還可以為后續進一步優化創建條件。

需要注意逃逸分析的性能收益是否低于它的消耗。如果要完全準確地判斷一個對象是否會逃逸,需要進行數據流敏感的一系列復雜分析,從而確定程序各個分支執行時對此對象的影響,這是一個相對耗時的過程,如果分析發現沒有幾個不逃逸的對象,那這些運行期耗用的時間就白白浪費了,所以目前虛擬機只能采用不那么準確,但時間壓力相對較小的算法來完成逃逸分析。

到此,關于“怎么使用java Hotspot虛擬機內的即時編譯器”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

元阳县| 正镶白旗| 宣汉县| 青河县| 云林县| 中江县| 安阳市| 五寨县| 乌什县| 福州市| 德格县| 宝清县| 怀化市| 清镇市| 大兴区| 海兴县| 灵丘县| 兴和县| 武鸣县| 沾益县| 长沙市| 集安市| 朔州市| 昌都县| 黎川县| 东海县| 张掖市| 邵阳市| 黔东| 贵港市| 宜城市| 出国| 万山特区| 兰西县| 衡南县| 太白县| 蕲春县| 岚皋县| 北京市| 德惠市| 武平县|