JVM系列三(運(yùn)行時數(shù)據(jù)區(qū))

Java虛擬機(jī)(JVM)系列三

運(yùn)行時數(shù)據(jù)區(qū)

一.運(yùn)行時數(shù)據(jù)區(qū)整體架構(gòu)

運(yùn)行時數(shù)據(jù)區(qū).png
  • JVM定義了若干種程序在運(yùn)行期間會使用到運(yùn)行時數(shù)據(jù)區(qū)啼染,其中有一些會隨著JVM啟動而創(chuàng)建,退出而銷毀(進(jìn)程)锚沸,另一些則與線程一一對應(yīng)笔时,這些與線程對應(yīng)的數(shù)據(jù)區(qū)會隨著線程的開始和結(jié)束而創(chuàng)建和銷毀(線程
  • 方法區(qū)和堆是進(jìn)程級別的颓屑,屬于被共享的
  • 棧粒褒、本地方法棧、程序計(jì)數(shù)器是每個線程獨(dú)有一份
  • 每個JVM只有一個運(yùn)行時實(shí)例诚镰,即Runtime實(shí)例
  • 在HotSpot虛擬機(jī)中奕坟,每個線程與操作系統(tǒng)的本地線程直接映射

二.程序計(jì)數(shù)器(也叫PC寄存器 不存在GC祥款、OOM)

1.作用
  • PC寄存器是用來存儲指向下一條指令的地址,由執(zhí)行引擎讀取指令并執(zhí)行
2.簡介
  • PC寄存器是一塊很小的空間月杉,幾乎可以忽略不計(jì)刃跛,也是運(yùn)行速度最快的存儲區(qū)域
  • 在JVM規(guī)范中,每個線程都有自己的的一個程序計(jì)數(shù)器
  • 任何時間一個線程都只有一個方法在執(zhí)行苛萎,即當(dāng)前方法
  • 字節(jié)碼解釋器工作時就是通過改變這個計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令
  • 它是唯一一個在JVM規(guī)范中沒有規(guī)定任何OutOfMemoryError的情況的區(qū)域


    PC寄存器使用舉例.png
3.面試時的問題
  • 使用PC寄存器存儲字節(jié)碼指令地址有什么用呢(為什么使用PC寄存器記錄當(dāng)前線程的執(zhí)行地址呢)桨昙?
    • 1)因?yàn)镃PU需要不停的切換各個線程,切換回來后腌歉,需要知道該執(zhí)行哪個指令了
    • 2)JVM解釋器需要通過改變PC寄存器的值來獲取下一條應(yīng)該執(zhí)行什么樣的字節(jié)碼指令
  • PC寄存器為什么被設(shè)定為私有的
    為了可以準(zhǔn)確記錄各個線程正在執(zhí)行的當(dāng)前字節(jié)碼指令地址蛙酪,確保各線程間獨(dú)立計(jì)算,不互相干擾
4.額外
  • 并行:一個時刻同時執(zhí)行翘盖,并行的關(guān)鍵是同時做很多事情
  • 并發(fā):一個時間段內(nèi)交替執(zhí)行桂塞,并發(fā)的關(guān)鍵是同時管理很多事情

三.棧(虛擬機(jī)棧 存在OOM,不存在GC)

1.簡介
  • Java虛擬機(jī)棧在每個線程創(chuàng)建的時候被創(chuàng)建馍驯,其內(nèi)部是一個個的棧幀阁危,對應(yīng)一次次的Java方法調(diào)用
  • 主管Java的運(yùn)行,它保存局部變量(8種基本數(shù)據(jù)類型汰瘫、對象的陰影)狂打、部分結(jié)果,并參與方法的調(diào)用和返回
2.棧的特點(diǎn)
  • 棧是一種快速有效的分配存儲結(jié)構(gòu)混弥,速度僅次于PC寄存器
  • 只有進(jìn)棧和出棧的操作趴乡,具有先進(jìn)后出的特點(diǎn)
3.棧與堆

棧解決的是數(shù)據(jù)運(yùn)行的問題(也存儲局部變量,中間結(jié)果)剑逃,堆解決的是數(shù)據(jù)存儲問題(主要存儲的是對象)

4.面試中遇到的問題
  • 開發(fā)中遇到的異常
    棧中可能出現(xiàn)的異常(StackOverflowError:自己調(diào)用自己 OutOfMemoryError)
5.設(shè)置棧參數(shù)
  • 設(shè)置棧大姓阋恕( -Xss256k ----設(shè)置棧大小為256k)
6.棧的存儲單位
  • 棧的存儲單位是棧幀,每個方法對應(yīng)一個棧幀(Stack Frame)
  • 三個名詞:當(dāng)前棧幀 當(dāng)前方法 當(dāng)前類
  • 執(zhí)行引擎運(yùn)行的所有字節(jié)碼指令只對當(dāng)前棧幀進(jìn)行操作
7.棧楨

每個棧幀中存儲著

  • 局部變量表(LC Local Variable)

  • 操作數(shù)棧(Operant Stack 或表達(dá)式棧)

  • 動態(tài)鏈接(Dynamic Linking 或指向運(yùn)行時常量池的方法引用)

  • 返回地址(Return Address 或方法正常退出或者異常退出的定義)

  • 一些附加信息

    7.1局部變量表(Local Variable)
    • 局部變量表也被稱為是局部變量數(shù)組或本地變量表
    • 定義為一個數(shù)字?jǐn)?shù)組蛹磺,主要存儲的是方法參數(shù)以及在方法內(nèi)聲明的局部變量(八大基本數(shù)據(jù)類型粟瞬,對象的 引用以及返回地址類型)
    • 局部變量表是創(chuàng)建在線程的棧上面的,是線程的私有數(shù)據(jù)萤捆,因此不存在數(shù)據(jù)安全問題
    • 局部變量表的大腥蛊贰(即數(shù)組長度)是在編譯的時候就定下來的,并且不可以改變
    • 局部變量表中的變量只在當(dāng)前方法內(nèi)有效
    • 局部變量表的基本存儲單元是Slot(變量槽)俗或,32位以內(nèi)的數(shù)據(jù)類型占用一個Slot(byte市怎、short、int辛慰、float区匠、char在存儲前被轉(zhuǎn)換為int、boolean也被轉(zhuǎn)換為int、以及返回地址類型)驰弄,64位的類型(long和double)占據(jù)兩個Slot
    • JVM會為局部變量表中的每一個Slot分配一個訪問索引麻汰,以便訪問到局部變量表中指定的局部變量值
    • 當(dāng)一個實(shí)例方法被調(diào)用的時候,它的方法參數(shù)以及內(nèi)部的局部變量都會被按照順序復(fù)制到局部變量表中的Slot上
    • 如果當(dāng)前棧幀是由構(gòu)造方法或者實(shí)例方法(即非靜態(tài)方法)創(chuàng)建的戚篙,那么該對象的引用this將會被存儲到index為0的slot處(所以當(dāng)我們在靜態(tài)方法中使用this時會報錯)
    • 棧幀中局部變量表中的Slot(槽位)是可以重復(fù)利用五鲫,如果一個局部變量過了作用域,那么在后面聲明的局部變量很可能會復(fù)用過期的局部變量的槽位岔擂,從而達(dá)到節(jié)省資源的目的
    • 局部變量中的變量也是重要的垃圾回收根節(jié)點(diǎn)位喂,只要被局部變量中直接或間接引用的對象都不會被回收
    7.2 操作數(shù)棧(Operand Stack)

    棧可以使用數(shù)組或者鏈表實(shí)現(xiàn)乱灵,只允許進(jìn)棧和出棧操作

  • 每一個獨(dú)立的棧幀中除了包括局部變量表塑崖,還包含一個先進(jìn)后出的操作數(shù)棧

  • 操作數(shù)棧,在方法執(zhí)行過程中阔蛉,根據(jù)字節(jié)碼指令,往棧中寫入數(shù)據(jù)或取出數(shù)據(jù)聋呢,即入棧和出棧

  • 操作數(shù)棧颠区,主要用于保存計(jì)算過程中的中間結(jié)果削锰,同時作為計(jì)算過程中變量的臨時存儲空間

  • 每一個操作數(shù)棧,都有一個確定的棧深毕莱,在編譯時就確定(max_stack的值)

  • 操作數(shù)棧數(shù)據(jù)的訪問是通過入棧和出棧實(shí)現(xiàn),而不是通過索引進(jìn)行訪問蛹稍,這與指定局部變量值的訪問不同

  • 被調(diào)用的方法如果有返回值,其返回值會被壓入當(dāng)前棧幀的操作數(shù)棧中部服,并更新PC寄存器中下一條需要執(zhí)行的字節(jié)碼指令

  • 我們所說的Java虛擬機(jī)的解釋引擎是基于棧的執(zhí)行引擎唆姐,這個棧就是值操作數(shù)棧

  • 棧頂緩存技術(shù)(為了解決數(shù)據(jù)頻繁進(jìn)棧出棧廓八,即內(nèi)存對數(shù)據(jù)頻繁的讀/寫操作進(jìn)而影響速度,提出的此技術(shù)剧蹂,它的核心思想是將棧頂元素全部緩存在物理CPU的寄存器中

    7.3 動態(tài)鏈接(Dynamic Linking 也叫做 指向運(yùn)行時常量池的方法引用)
動態(tài)鏈接.png
  • 每一個棧幀內(nèi)部都包含一個指向運(yùn)行時常量池的該棧幀所屬方法的引用宠叼。其目的是為了當(dāng)前方法的代碼能實(shí)現(xiàn)動態(tài)鏈接(如 invokerdynamic指令)
  • java源文件被編譯成字節(jié)碼文件的時候,所有的變量和方法都作為符號引用【類加載子系統(tǒng)鏈接階段的解析步驟】保存在class文件的常量池里。動態(tài)鏈接的作用就是將這些符號引用轉(zhuǎn)換為調(diào)用方法的直接引用
7.4 方法返回地址(Return Address)
  • 存放調(diào)用該方法的pc寄存器的值(pc寄存器存儲的是該方法要執(zhí)行的下一條指令的值)醋闭,即調(diào)用該方法的指令的下一條指令的地址朝卒。
  • 方法的正常退出和異常推出的區(qū)別是:通過異常完成出口退出的 給他的上層調(diào)用者產(chǎn)生任何的返回值乐埠。
  • 一個方法在正常調(diào)用完成后酒精返回哪一一個返回指令需要根據(jù)具體的返回值類型確定丈咐。
  • 在字節(jié)碼指令中,返回指令包括
    ireturn:返回值類型是 byte棵逊、char、short徒像、int蛙讥、boolean
    freturn:返回值類型是float
    dreturn:返回值類型是double
    areturn:返回值類型是引用類型
    return:該方法為void方法、實(shí)例初始化方法【構(gòu)造器】次慢、類和接口的初始化方法【構(gòu)造器】)
7.5 一些附加信息

棧幀中還允許攜帶與JVM實(shí)現(xiàn)相關(guān)的一些附加信息旁涤。如對程序調(diào)試提供支持的信息

8.方法調(diào)用
  • 在JVM中迫像,將符號引用轉(zhuǎn)換為調(diào)用方法的直接引用與方法的綁定機(jī)制有關(guān)
    綁定:一個字段、方法或類的符號引用被替換為直接引用的過程菌羽,僅發(fā)生一次
    靜態(tài)鏈接:當(dāng)一個字節(jié)碼文件被裝載到JVM內(nèi)部時纷闺,如果被調(diào)用的目標(biāo)方法,在編譯期可知犁功,且運(yùn)行期保持不變。這種情況下將調(diào)用方法的符號引用轉(zhuǎn)換為直接引用的過程叫靜態(tài)鏈接署鸡,對應(yīng)的綁定機(jī)制是早期綁定
    動態(tài)鏈接:如果在被調(diào)用的方法在編譯期不確定,只能在程序運(yùn)行的期將調(diào)用方法的符號引用轉(zhuǎn)換為直接引用时捌。這種轉(zhuǎn)換過程具備動態(tài)性炉抒,因此稱之為動態(tài)鏈接,對應(yīng)的綁定機(jī)制是晚期綁定
  • 非虛方法與虛方法
    如果方法在編譯期就確定了調(diào)用的版本拿诸,在運(yùn)行時保持不變塞茅,則該方法叫非虛方法
    靜態(tài)方法、final修飾的方法描沟、私有方法鞭光、實(shí)例構(gòu)造器、父類方法都是非虛方法迟蜜,其他都是虛方法
  • 虛擬機(jī)中提供了以下方法的調(diào)用指令
    invokestatic:調(diào)用靜態(tài)方法啡省,解析階段確定唯一版本
    invokespecial:調(diào)用<init>方法卦睹、私有及父類方法、解析階段確定唯一版本
    invokevirtual: 調(diào)用所有虛方法
    invokeinterface: 調(diào)用接口方法
    invokedynamic:動態(tài)解析出需要調(diào)用的方法障斋,然后執(zhí)行徐鹤。Java8中的Lamda表達(dá)式的出現(xiàn),使得此指令的生成遂庄,在Java中才有了直接的生成方法
    invokestaticinvokespecial指令調(diào)用的方法叫非虛方法劲赠,其余的(final修飾的方法除外)稱為虛方法
  • 靜態(tài)類型語言與動態(tài)類型語言
    靜態(tài)類型語言與動態(tài)類型語言的區(qū)別在于對于類型的檢查是在編譯期還是在運(yùn)行期秸谢,滿足前者的是靜態(tài)類型語言估蹄。靜態(tài)類型語言是判斷變量本身的類型信息沫换,動態(tài)類型語言是判斷變量值的類型信息。變量沒有類型信息刊棕,變量值才有類型信息待逞,這是動態(tài)語言的一個重要特性(如JS)
  • 重寫(是動態(tài)分派的典型代表)
    1)當(dāng)調(diào)用一個對象的方法的時候网严,我們會將對象壓入操作數(shù)棧,根據(jù)字節(jié)碼指令(invokevirtual)怜庸,尋找該對象的實(shí)際類型垢村,記做C
    2)如果在類型C中找到與常量池中描述符合簡單名稱都符合的方法嘉栓,則進(jìn)行訪問權(quán)限的校驗(yàn),如果通過侵佃,則返回該方法的直接引用馋辈,若不通過,則返回java.lang.IllegalAcessEror異常
    3)若在常量中未找到符合的方法叉抡,按照繼承關(guān)系從下往上依次對C 的父類進(jìn)行第二步的搜索和驗(yàn)證答毫。
    4)如果始終未找到,則拋出java.lang.AbstrackMethodError異常
  • 虛方法表
    在面向?qū)ο缶幊讨兄嵘樱瑫l繁的使用動態(tài)分派,如果每次在動態(tài)分派過程中都進(jìn)行搜索和驗(yàn)證的步驟侦锯,必然會影響到執(zhí)行效率秦驯,為了提升性能,JVM在類的方法區(qū)建立一個虛方法表
    每個類中都有一個虛方法表亲桥,表中存放各個方法的實(shí)際入口固耘,即真正調(diào)用的放方法
    虛方法表在類加載的鏈接階段的解析步驟中被創(chuàng)建并初始化
    虛方法表1.png

    如圖撤防,cat()類中的toString()是重寫Object類中方法村砂,所以toString()實(shí)際所屬類型是Cat類的
9.虛擬機(jī)棧的面試題
  • 舉例棧溢出的情況(StackOverFlowError)葫笼?
    通過 -Xss設(shè)置棧的大小
  • 調(diào)整棧大小路星,就能保證不出現(xiàn)棧溢出么诱桂?
    不能保證〉姘ぃ可以設(shè)置棧大小確保出現(xiàn)的幾率降低触菜,但不能保證不初選棧溢出
  • 分配的棧內(nèi)存越大越好么?
    不是的哲泊〈呋龋總內(nèi)存數(shù)是一定的,如果某個棧內(nèi)存設(shè)置太大先朦,會擠壓其他內(nèi)存結(jié)構(gòu)的空間
  • 垃圾回收是否會涉及到虛擬機(jī)棧
    不會。
名稱 error gc
程序計(jì)數(shù)器 不出現(xiàn) 不出現(xiàn)
虛擬機(jī)棧 出現(xiàn) 不出現(xiàn)
本地方法棧 出現(xiàn) 不出現(xiàn)
方法區(qū) 出現(xiàn) 出現(xiàn)
出現(xiàn) 出現(xiàn)
  • 方法中定義的局部變量是否線程安全?
    具體問題具體分析刺彩。(如果局部變量是內(nèi)部產(chǎn)生的创倔,并且在方法內(nèi)消亡的,則是線程安全的畦攘。如果是作為參數(shù)傳進(jìn)來的知押,或者作為方法返回值返回,則會存在線程安全問題)
    何為線程安全?
    --如果只有一個縣城去操作此數(shù)據(jù)偎漫,則此數(shù)據(jù)必是線程安全的象踊。
    --如果多個線程操作此數(shù)據(jù),則此數(shù)據(jù)是共享數(shù)據(jù)栈虚。如果不考慮同步機(jī)制的話史隆,會存在線程安全問題

四.本地方法棧

Java虛擬機(jī)棧是用來管理Java程序的調(diào)用,而本地方法棧是用來管理本地方法的調(diào)用

  • 本地方法棧也是線程私有的
  • 當(dāng)我們調(diào)用本地方法的時候粘姜,會將此本地方法壓入本地方法棧中熔酷,在執(zhí)行引擎執(zhí)行時加載本地方法庫

五.本地方法接口(不屬于運(yùn)行時數(shù)據(jù)區(qū))

  • 一個Native Mehod 就是 Java調(diào)用非Java代碼的接口
  • 在定義一個Native Mehotd的時候拒秘,不需要提供 實(shí)現(xiàn)體臭猜,因?yàn)閷?shí)現(xiàn)體是由非Java語言在外面實(shí)現(xiàn)的
    thread押蚤、Object等類中有多個本地方法
  • 標(biāo)識符native可以與所有其他的java標(biāo)識符連用, abstract除外(abstract是沒有實(shí)現(xiàn)體丐膝,但是native是有實(shí)現(xiàn)體的钾菊,只不過是用非java語言實(shí)現(xiàn)的 )
  • 為什么要使用本地方法煞烫?
    • 1.與Java外環(huán)境交互
      有時Java應(yīng)用要與Java外環(huán)境交互,這是本地方法存在的主要原因
    • 2.與操作系統(tǒng)交互
      Java代碼是運(yùn)行在JVM上的凛俱,但是JVM畢竟不是一個完整的系統(tǒng)料饥,經(jīng)常依賴一些底層系統(tǒng)的支持,使用本地方法原叮,我們得以使用Java實(shí)現(xiàn)了jre與底層系統(tǒng)的交互巡蘸。還有我們可以通過本地方法使用java語言本身沒有提供封裝的操作系統(tǒng)的特性悦荒。
    • 3.Sun's Java
      sun的解釋器本身使用C語言編寫的
      六.堆
  • 堆的知識點(diǎn)多,單獨(dú)拿一小節(jié)細(xì)說
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末境氢,一起剝皮案震驚了整個濱河市身腻,隨后出現(xiàn)的幾起案子嘀趟,更是在濱河造成了極大的恐慌,老刑警劉巖牛隅,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匕累,居然都是意外死亡默伍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狸剃,“玉大人钞馁,你說我怎么就攤上這事√骄保” “怎么了训措?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵隙弛,是天一觀的道長狞山。 經(jīng)常有香客問我,道長总珠,這世上最難降的妖魔是什么勘纯? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任驳遵,我火速辦了婚禮,結(jié)果婚禮上唆迁,老公的妹妹穿的比我還像新娘。我一直安慰自己鳞溉,他們只是感情好鼠哥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布朴恳。 她就那樣靜靜地躺著,像睡著了一般贞绵。 火紅的嫁衣襯著肌膚如雪恍飘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天母蛛,我揣著相機(jī)與錄音彩郊,去河邊找鬼蚪缀。 笑死,一個胖子當(dāng)著我的面吹牛违帆,可吹牛的內(nèi)容都是我干的金蜀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼尝胆,長吁一口氣:“原來是場噩夢啊……” “哼含衔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逊桦,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤强经,失蹤者是張志新(化名)和其女友劉穎寺渗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炬称,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涡拘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年鳄乏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朽缴。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡密强,死狀恐怖蜗元,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情劳坑,我是刑警寧澤成畦,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布循帐,位于F島的核電站舀武,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瘪匿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顽染。 院中可真熱鬧,春花似錦尼荆、人聲如沸唧垦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狞悲。三九已至妇斤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荸恕,已是汗流浹背死相。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工算撮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人陷舅。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓审洞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親仰剿。 傳聞我的和親對象是個殘疾皇子南吮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容