02 | Java 代碼是怎么運行的牌里?

Java 很多不同運行方式,但 都離不開JRE 务甥,Java 運行環(huán)境牡辽。

  • 開發(fā)工具運行
  • 雙擊執(zhí)行Jar 文件運行
  • 命令行運行
  • 網(wǎng)頁中運行

JRE : 包含必要組件,Java虛擬機敞临,Java 核心類庫态辛。
JDK : Java 開發(fā)工具包,包含JRE 挺尿,附帶一系列開發(fā)奏黑、診斷工具

Java 執(zhí)行系統(tǒng)主流實現(xiàn)以及設(shè)計決策

C++ 可以直接編譯成機器碼運行炊邦,Java為什么還需要虛擬機中運行呢?

  • Java 是高級程序語言熟史,語法非常復(fù)雜馁害,抽象度很高,執(zhí)行在硬件運行復(fù)雜的程序不現(xiàn)實蹂匹,所以運行之前需要轉(zhuǎn)換

轉(zhuǎn)換設(shè)計思路:

設(shè)計面向 java 語言特性的虛擬機碘菜,并通過編譯器將Java 程序轉(zhuǎn)換成該虛擬機能識別的 指令序列。就是Java 字節(jié)碼限寞。
取名來歷: 因為Java 字節(jié)碼 指令的操碼 (opcode) 被固定位一個字節(jié)忍啸。

  • Java 虛擬機可以由硬件實現(xiàn),但更多是軟件實現(xiàn)履植,一旦程序被轉(zhuǎn)換成Java 字節(jié)碼计雌,便可以在不同平臺虛擬機里面運行,我們說的 一次編寫玫霎,到處運行凿滤。

  • 帶來了一個托管環(huán)境,能代替我們處理一些代碼冗長而且容易出錯的部分鼠渺。
    其中當屬自動內(nèi)存管理鸭巴,與 垃圾回收。
    除此之外拦盹,托管環(huán)境還提供諸如數(shù)組越界鹃祖、動態(tài)類型、安全權(quán)限等動態(tài)監(jiān)測普舆,免于書寫這些業(yè)務(wù)無關(guān)的邏輯代碼恬口。

Java虛擬機具體怎么運行的java 代碼?

以標準JDK 中的 HotSpot 虛擬機為例沼侣,從虛擬機以及底層硬件兩個角度祖能,講Java 虛擬機具體是怎么運行Java 字節(jié)碼的。

  • 虛擬機 視角
  1. 執(zhí)行Java 代碼蛾洛,要將它編譯而成的Class 文件加載到Java 虛擬機中养铸。

  2. 加載后Java 類會存放在 方法去(Method Area) 中。實際運行時轧膘,虛擬機會執(zhí)行方法區(qū)內(nèi)的代碼钞螟。

  3. Java 虛擬機在內(nèi)存中劃分出堆和棧來存儲運行時的數(shù)據(jù)。
    將棧細分為Java方法的Java方法棧谎碍,和面向本地方法(用C++ 寫的native)的本地方法棧鳞滨。
    以及存放各個線程執(zhí)行位置的PC 寄存器。

image.png
  1. 運行過程中蟆淀,每當調(diào)用一個Java 方法拯啦,Java 虛擬機會在當前線程的Java 方法棧中生成一個棧幀澡匪,用以存放局部變量以及字節(jié)碼操作數(shù)。
    棧幀大小提前計算好的褒链,虛擬機不要錢棧幀在內(nèi)存工具里連續(xù)分布唁情。

  2. 當退出當前執(zhí)行方法時,不管正常返回還是異常返回碱蒙,虛擬機均會彈出當前線程的當前棧幀荠瘪,并將其舍棄。

  • 從硬件 視角
  1. Java 字節(jié)碼無法直接執(zhí)行赛惩,Java 虛擬機需要將字節(jié)碼翻譯成機器碼。
    在Hotspot 里面趁餐,翻譯過程有兩種形式:
    第一種:解釋執(zhí)行喷兼,即逐條將字節(jié)碼翻譯成機器碼并執(zhí)行。
    第二種:即時編譯(Just-In-Time compilation 后雷,JIT) 季惯,將一個方法中包含的所有字節(jié)碼貶義詞機器碼后再執(zhí)行。

前者優(yōu)勢在于無需等待編譯臀突,而后者優(yōu)勢在于實際運行速度更快勉抓。

Hotspot 默認采用混合模式,綜合解釋執(zhí)行和即時編譯的優(yōu)點候学。 會先解釋執(zhí)行字節(jié)碼藕筋,而后將其中反復(fù)執(zhí)行的熱點代碼,以方法為單位進行即時編譯梳码。

image.png

運行效率如何隐圾?

Hotspot 采用了多種技術(shù)提升啟動性能以及峰值性能,即時編譯是其中最重要的技術(shù)之一掰茶。

  • 即時編譯 建立在程序符合二八定律的假設(shè)上暇藏,就是20%代碼占據(jù)了80%的計算資源。
    80%不常用的代碼濒蒋,需要耗費時間將其編譯成機器碼盐碱,采取解釋執(zhí)行方式運行。

  • 理論上 即時編譯后 Java 程序的執(zhí)行效率 可能超過 c++ 程序的沪伙,
    因為瓮顽,與靜態(tài)編譯相比,即時編譯擁有程序的運行時信息焰坪,并能根據(jù)信息作出相應(yīng)的優(yōu)化趣倾。

舉例,虛方法用來實現(xiàn)對象多態(tài)性的某饰,一個虛方法調(diào)用儒恋,盡管很多目標方法善绎,但實際運行過程可能只調(diào)用其中一個,這個信息被即時編譯利用诫尽,來規(guī)避虛方法調(diào)用開銷禀酱,從而達到比靜態(tài)編譯的C++ 程序更高的性能。

如何規(guī)避的牧嫉?是把所有目標方法提前全部即時編譯?

  • 為了滿足不同用戶場景需要Hotspot 內(nèi)置了多個即時編譯器剂跟,C1 C2 和Graal。
    引入多個即時編譯器酣藻,
    為了編譯時間和生成代碼的執(zhí)行效率進行取舍曹洽。
  • C1: Client 編譯器 ,面向的是對啟動性能有要求的客戶端GUI 程序辽剧,采用的優(yōu)化手段相對簡單送淆,所以編譯時間較短。
    C2:Server 編譯器怕轿,面向的是對峰值性能有要求的服務(wù)器端程序偷崩,采用的優(yōu)化手段相對復(fù)雜,因此編譯時間較長撞羽,但同時生成代碼的執(zhí)行效率較高阐斜。

Java 7 開始分層編譯,熱點方法首先C1 編譯诀紊,然后熱點方法中的熱點會進一步被C2編譯

  • 為了不干擾應(yīng)用的正常運行谒出,Hotspot即時編譯是放在額外的編譯線程中進行的。
    Hotspot根據(jù)CPU 的數(shù)量設(shè)置編譯線程的數(shù)目渡紫,并且按1:2 的比例配置給 C1 及 C2 編譯器到推。

  • 在計算資源充足的情況下,字節(jié)碼解釋執(zhí)行和即時編譯同時進行惕澎,編譯完成后機器碼會在下次調(diào)用該方法時啟用莉测,以替換原本的解釋執(zhí)行。


作業(yè):思考Java語言 和Java虛擬機看到Boolean類型的方式是否不同唧喉。

$ echo '
public class Foo {
 public static void main(String[] args) {
  boolean flag = true;
  if (flag) System.out.println("Hello, Java!");
  if (flag == true) System.out.println("Hello, JVM!");
 }
}' > Foo.java
$ javac Foo.java
$ java Foo
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1
$ awk 'NR==1,/iconst_1/{sub(/iconst_1/, "iconst_2")} 1' Foo.jasm.1 > Foo.jasm
$ java -cp /path/to/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm
$ java Foo

----------------------- 解釋 ----------------------------
jvm把boolean當做int來處理

flag = iconst_1 = true

awk把stackframe中的flag改為iconst_2

if(flag)比較時ifeq指令做是否為零判斷捣卤,常數(shù)2仍為true,打印輸出

if(true == flag)比較時if_cmpne做整數(shù)比較八孝,iconst_1是否等于flag董朝,比較失敗,不再打印輸出


精彩評論:

  • 解釋執(zhí)行 執(zhí)行時才翻譯成機器指令干跛,無需保存不占內(nèi)存子姜。但即時編譯類似預(yù)編譯,編譯之后的指令需要保存在內(nèi)存中楼入,這種方式吃內(nèi)存哥捕,按照二八原則這種混合模式最恰當?shù)哪脸椋瑹狳c代碼編譯之后放入內(nèi)存避免重復(fù)編譯,而其他運行次數(shù)較少代碼則解釋執(zhí)行遥赚,避免占用過多內(nèi)存

  • 為什么不把代碼全部編譯成機器碼扬舒?

問得好!事實上JVM確實有考慮做AOT (ahead of time compilation) 這種事情凫佛。AOT能夠在線下將Java字節(jié)碼編譯成機器碼讲坎,主要是用來解決啟動性能不好的問題。

對于這種發(fā)布頻率不頻繁(也就是長時間運行吧愧薛?)的程序晨炕,其實選擇線下編譯和即時編譯都一樣,因為至多一兩個小時后該即時編譯的都已經(jīng)編譯完成了毫炉。另外府瞄,即時編譯器因為有程序的運行時信息,優(yōu)化效果更好碘箍,也就是說峰值性能更好。

  • 熱點代碼區(qū)別

看到有人說熱點代碼的區(qū)別鲸郊,在git里面涉及到的熱點代碼有兩種算法丰榴,
基于采樣的熱點探測和基于計數(shù)器的熱點探測。
一般采用的都是基于計數(shù)器的熱點探測秆撮,兩者的優(yōu)缺點百度一下就知道了四濒。
基于計數(shù)器的熱點探測又有兩個計數(shù)器:
方法調(diào)用計數(shù)器,回邊計數(shù)器职辨,他們在C1和C2又有不同的閾值盗蟆。????

  • 什么時候使用C1,什么時候使用C2舒裤,他是怎么區(qū)分熱點方法的呢喳资?

作者回復(fù): 剛剛看到一個同學(xué)總結(jié)了。JVM會統(tǒng)計每個方法被調(diào)用了多少次腾供,超過多少次仆邓,那就是熱點方法。(還有個循環(huán)回邊計數(shù)器伴鳖,用來編譯熱循環(huán)的节值。)

默認的分層編譯應(yīng)該是達到兩千調(diào)C1,達到一萬五調(diào)C2榜聂。

  • 棧幀

老師搞疗,那個pc寄存器,本地方法棧须肆,以及方法棧匿乃,java方法棧這三個組成的就是我們常統(tǒng)稱的棧吧桩皿,然后也叫棧幀?

作者回復(fù): JVM里的棧指的應(yīng)該是Java方法棧和本地方法棧扳埂。每個方法調(diào)用會在棧上劃出一塊作為棧幀(stack frame)业簿。棧是由多個棧幀構(gòu)成的,就好比電影是由一個個幀構(gòu)成的阳懂。

  • 為啥是"理論"上比cpp快...這樣看起來 如果都編譯成機器碼了 應(yīng)該就是挺快的呀... 那干啥不像Go一樣 直接編譯成目標平臺的機器碼... 咋感覺繞了一圈..

作者回復(fù): 因為實際上會插入一些虛擬機相關(guān)的代碼梅尤,稍微拉低了運行效率。

至于為什么不采用直接編譯的方法岩调,在峰值性能差不多的這個前提下巷燥,線下編譯和即時編譯就是兩種選項,各有優(yōu)缺點号枕。JVM這樣做缰揪,主要也是看重字節(jié)碼的可移植性,而犧牲了啟動性能葱淳。

另外呢钝腺,現(xiàn)代工程語言實現(xiàn)都是抄來抄去的。JVM也引入了AOT編譯赞厕,在線下將Java代碼編譯成可鏈接庫艳狐。

  • 老師,問一下這個asmtools是做什么用的

作者回復(fù): 就是Java字節(jié)碼的反匯編器和匯編器皿桑。

  • 解釋執(zhí)行是將字節(jié)碼翻譯為機器碼毫目,JIT也是將字節(jié)碼翻譯為機器碼,為什么JIT就比解釋執(zhí)行要快這么多诲侮?
    如果說JIT檢測到是熱點代碼并且進行優(yōu)化镀虐,那么為什么解釋執(zhí)行不直接就用這種優(yōu)化去解釋字節(jié)碼?
    一些比較淺的問題沟绪,希望老師能指點一二

作者回復(fù): 1. 就單條加法字節(jié)碼而言刮便,解釋執(zhí)行器需要識別字節(jié)碼,然后將兩個操作數(shù)從Java方法棧上讀取出來并相加近零,最后將結(jié)果存入Java方法棧中诺核。而JIT生成的機器碼就只是一個CPU加法指令。

  1. 因為JIT比較費時久信。如果字節(jié)碼需要JIT后才跑窖杀,那么啟動性能會很糟糕
  • 對于占據(jù)大部分的不常用的代碼,我們無需耗費時間將其編譯成機器碼裙士,而是采取解釋執(zhí)行的方式運行入客;
    這是否意味著不常用的代碼的多次調(diào)用就要多次進行解釋執(zhí)行

作者回復(fù): 調(diào)用到一定次數(shù)就會觸發(fā)即時編譯的

  • 對不起,聽了29篇文章了,至今不太清楚hotspot和openjdk兩者之間的關(guān)系桌硫。

作者回復(fù): HotSpot是JVM里的引擎夭咬,可以理解為JDK中用C++寫的部分。Oracle JDK/OpenJDK包括HotSpot铆隘。

  • 搞不懂卓舵,沒有講清楚堆棧到底如何共享?有些文章說棧數(shù)據(jù)共享膀钠,但又說每個線程都會有一個堆棧掏湾,那堆棧的數(shù)據(jù)還如何共享?還有堆有時候說數(shù)據(jù)不共享肿嘲,但又說線程間數(shù)據(jù)共享融击?這老師能解答一下嗎?

作者回復(fù): 線程各自的楒撸空間是不共享的尊浪,但可以通過堆空間來共享數(shù)據(jù)。如果只有一個線程知道某個數(shù)據(jù)存放在堆的哪個位置封救,那也相當于不共享拇涤。注意不是等同于不共享,因為其它線程可以掃描整個堆誉结,來找到這個位置工育。

  • 方法區(qū)是不是屬于堆的一部分?

作者回復(fù): 不屬于搓彻。JVM中的堆是用來存放Java對象的。

-老師你好嘱朽,我有個地方還是想不通旭贬,為什么java采用一次編譯,到處運行的這種方式搪泳,而不是C++的不同平臺都進行編譯稀轨, java這樣設(shè)計 加了中間層 反而執(zhí)行效率降低,那這種設(shè)計的初衷是什么呢岸军?

作者回復(fù): 個人感覺應(yīng)該是靜態(tài)編譯的各種語言中C++比較突出奋刽,一次編譯到處運行的各種語言中Java比較典型。

你可以用LLVM把C++編譯成bitcode到處運行艰赞,也可以用AOT把Java編譯成機器碼佣谐。只不過不是那么”流行”

-老師,即時編譯是啥算法方妖?編譯哪些代碼狭魂?何時編譯完成?為啥我每次壓測啟動后,top命令查看雌澄,同樣的代碼編譯線程工作時長不太一樣斋泄?

作者回復(fù): 即時編譯就是一個編譯器,里面有很多不同的優(yōu)化镐牺,對應(yīng)不同的算法炫掐。觸發(fā)即時編譯用的是JVM維護的統(tǒng)計方法調(diào)用次數(shù)的計數(shù)器。編譯時間取決于編譯器自己的效率睬涧。由于程序的不確定性募胃,在多線程環(huán)境下即時編譯器干的活可能多可能少。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宙地,一起剝皮案震驚了整個濱河市摔认,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宅粥,老刑警劉巖参袱,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異秽梅,居然都是意外死亡抹蚀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門企垦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來环壤,“玉大人,你說我怎么就攤上這事钞诡≈O郑” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵荧降,是天一觀的道長接箫。 經(jīng)常有香客問我,道長朵诫,這世上最難降的妖魔是什么辛友? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮剪返,結(jié)果婚禮上废累,老公的妹妹穿的比我還像新娘。我一直安慰自己脱盲,他們只是感情好邑滨,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钱反,像睡著了一般驼修。 火紅的嫁衣襯著肌膚如雪殿遂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天乙各,我揣著相機與錄音墨礁,去河邊找鬼。 笑死耳峦,一個胖子當著我的面吹牛恩静,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹲坷,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼驶乾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了循签?” 一聲冷哼從身側(cè)響起级乐,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎县匠,沒想到半個月后风科,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡乞旦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年贼穆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兰粉。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡故痊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玖姑,到底是詐尸還是另有隱情愕秫,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布焰络,位于F島的核電站豫领,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舔琅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一洲劣、第九天 我趴在偏房一處隱蔽的房頂上張望备蚓。 院中可真熱鬧,春花似錦囱稽、人聲如沸郊尝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流昏。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間况凉,已是汗流浹背谚鄙。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刁绒,地道東北人闷营。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像知市,于是被迫代替她去往敵國和親傻盟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350