真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JVM運行時數(shù)據(jù)區(qū)-創(chuàng)新互聯(lián)

文章目錄
  • 圖形概述
  • 程序計數(shù)器
    • 概念
    • 示例
    • 討論
  • 虛擬機棧(本地方法棧)
    • 棧幀概述
    • 棧幀結構
      • 局部變量表
        • Variable Slot
      • 操作數(shù)棧
        • JVM基于棧的字節(jié)碼解釋執(zhí)行引擎
      • 動態(tài)連接
      • 方法返回地址
      • 附加信息
    • 棧運行原理
    • 本地方法棧
    • 分代思想
    • 設置堆內(nèi)存的參數(shù)
    • 內(nèi)存分配策略
    • 對象在分代空間中流轉
  • 方法區(qū)(元空間)
    • 概述
    • 設置元空間內(nèi)存大小
    • 運行時常量池
  • 虛擬機棧、堆、方法區(qū)交互

本文基于JDK8進行分析

創(chuàng)新互聯(lián)是網(wǎng)站建設技術企業(yè),為成都企業(yè)提供專業(yè)的成都網(wǎng)站設計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設,網(wǎng)站設計,網(wǎng)站制作,網(wǎng)站改版等技術服務。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制適合企業(yè)的網(wǎng)站。十余年品質(zhì),值得信賴!圖形概述

忘了什么時候,在哪里聽到這么一句話,一圖勝千言,從后來的工作經(jīng)歷中驗證,圖形展示的內(nèi)容確實會加深記憶,不容易忘記。

先上圖片,運行時數(shù)據(jù)區(qū)布局概覽,元空間和CodeCache(JIT編譯后的代碼)又稱為非堆區(qū)域。
在這里插入圖片描述
在Hotspot虛擬中,本地方法棧已合并到虛擬機棧,上圖用相同顏色標記。
對運行時數(shù)據(jù)區(qū)有了整體的認知后,在來一張從線程安全角度來看運行時數(shù)據(jù)區(qū)的圖片,加深下記憶,接下來本篇會從程序計數(shù)器、虛擬機棧、堆、元空間進行詳細講解,ThreadLocal后續(xù)再開一片單獨講解,或是放到JUC中講解。
在這里插入圖片描述

程序計數(shù)器 概念
  • 可以看做當前線程所執(zhí)行的字節(jié)碼的行號指示器。
  • 可以忽略不記的內(nèi)存空間占用,運行最快的存儲區(qū)域。
  • 線程私有的,生命周期和線程生命周期保持一致。
  • 唯一一個在JVM規(guī)范中沒有規(guī)定任何OOM的區(qū)域。
  • 程序控制流的指示器,分支、循環(huán)、跳轉、異常處理、線程恢復等基礎功能都要依賴這個計數(shù)器完成。
  • 如果線程正在執(zhí)行的是一個Java方法,計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在指定Native方法,這個計數(shù)器值則為空(Undefined)。
示例
public static void main(String[] args) {int a = 1;
        int b = 2;
        System.out.println(a + b);
    }

有兩種方式可以查看到程序計數(shù)器,在終端可以執(zhí)行javap -v ProgramCounterRegister.class,或是使用jclasslib插件。
在這里插入圖片描述

討論
  1. 使用程序計數(shù)器存儲字節(jié)碼指令地址有什么用?
    CPU在取得時間片的線程間不停切換,切換回來的時候要知道從哪里繼續(xù)執(zhí)行。
  2. 程序計數(shù)器為何被設置為線程私有?
    CPU不停的做任務切換,這樣必然會導致經(jīng)常中斷或恢復,為了能準確記錄各個線程正在執(zhí)行的當前字節(jié)碼指令地址,最好的辦法就是每個線程都有自己的程序計數(shù)器。線程間不會相互影響。
虛擬機棧(本地方法棧) 棧幀概述
  • 每個線程都有自己的棧,棧中的數(shù)據(jù)都是以棧幀的形式存在,在線程上正在執(zhí)行的方法都有各自對應的一個棧幀。
  • 棧幀是一塊內(nèi)存區(qū)塊,是一個數(shù)據(jù)集,保存方法執(zhí)行過程中的各類數(shù)據(jù)信息。
棧幀結構
  • 局部變量表(Local Variables Table)
  • 操作數(shù)棧(Operand Stack)
  • 動態(tài)連接(Dynamic Linking,指向運行時常量池的方法引用)
  • 方法返回地址(方法正常退出或異常退出的定義)
  • 附加信息
局部變量表
  • 是一組變量值的存儲空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。
  • 容量大小在編譯期確定,在方法運行期間不會改變局部變量表大小。
  • 方法嵌套調(diào)用的次數(shù)由棧的大小決定,方法的參數(shù)和局部變量越多,局部變量表越大,進而棧幀越大,會占用更多的棧空間,導致嵌套次數(shù)減少。
  • 局部變量表中的變量只在當前方法調(diào)用中有效,方法執(zhí)行時,虛擬機通過局部變量表完成實參到形參的傳遞,方法調(diào)用結束后,隨著方法棧幀的銷毀,局部變量表也會隨之銷毀。
Variable Slot
  • 局部變量表的基本存儲單元是變量槽(Variable Slot),局部變量表中存放編譯期可知的8種基本數(shù)據(jù)類型,以及refrence引用類型,returnAddress類型的變量。
  • 32位以內(nèi)的類型只占一個slot,64位類型(long、double)占用兩個slot,byte、short、char存儲前轉換為int,boolean也被轉換為int,0表示false,非0表示true。
操作數(shù)棧
  • 操作數(shù)棧就是JVM執(zhí)行引擎的一個工作區(qū),當一個方法開始執(zhí)行時,一個新的棧幀會隨之創(chuàng)建,棧幀中的操作數(shù)棧也便創(chuàng)建了,只不過此時操作數(shù)棧是空的。
  • 在方法執(zhí)行過程中,根據(jù)字節(jié)碼指令,向棧中寫入數(shù)據(jù)或提取數(shù)據(jù),即入站(push)或出棧(pop)。
  • 主要用于保存計算過程的中間結果,同時作為計算過程中變量臨時的存儲空間。
  • 在編譯期確定棧深度用于存儲數(shù)值,保存在方法的Code屬性中。
  • 如果調(diào)用的方法有返回值,其返回值將會被壓入當前棧幀的操作數(shù)棧中,并更新程序計數(shù)器中下一條需要執(zhí)行的字節(jié)碼指令。
JVM基于棧的字節(jié)碼解釋執(zhí)行引擎

標題中棧指的就是操作數(shù)棧,本小節(jié)將展示基于棧的解釋器執(zhí)行過程,下面引用《深入理解Java虛擬機》(第三版)中的示例進行展示。

// 代碼
    public int calc() {int a = 100;
        int b = 200;
        int c = 300;
        return (a + b) * c;
    }

字節(jié)碼指令,截圖中顯示這段代碼需要深度為2的操作數(shù)棧和4個變量槽的局部變量空間。
在這里插入圖片描述
代碼執(zhí)行過程中,操作數(shù)棧、局部變量表的變化如圖1至圖6所示。
在這里插入圖片描述
圖1,執(zhí)行程序計數(shù)器地址為0的指令,bipush指令作用是將100壓入操作數(shù)棧頂。
圖2,執(zhí)行程序計數(shù)器地址為2的指令,istore_1指令作用是將操作數(shù)棧頂?shù)恼椭党鰲2⒋娣诺骄植孔兞勘淼刂窞?的變量槽中。
圖3,執(zhí)行程序計數(shù)器地址為11的指令,iload_1的作用是將局部變量表中地址為1的變量槽中的整型值100復制到操作數(shù)棧頂。
圖4,和圖3相同操作,將200復制到操作數(shù)棧頂。
圖5,iadd的作用是將操作數(shù)棧中頭兩個棧頂元素出棧,做整型加法,然后把結果重新入棧。iadd執(zhí)行完畢,100、200被出棧,計算結果300被重新入棧。
圖6,和圖3相同操作,將局部變量表中地址為3的變量槽中的300壓入操作數(shù)棧,imul是將兩個300出棧,然后做整型乘法,再將結果90000壓入操作數(shù)棧,ireturn將90000返回給調(diào)用者。

動態(tài)連接
  • 每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方 法調(diào)用過程中的動態(tài)連接(Dynamic Linking)。
  • 在Java源文件被編譯到字節(jié)碼文件中,所有的變量和方法引用都作為符號引用保存在class文件的常量池里。當一個方法調(diào)用其他方法時,就是通過常量池中指向方法的符號引用來表示的,動態(tài)連接的作用就是為了將這些符號引用轉換為調(diào)用方法的直接引用。
方法返回地址

一個方法執(zhí)行后有兩種方式退出。

  1. 正常退出,此時可能會有返回值傳遞給調(diào)用者。
  2. 異常退出,不會給上層調(diào)用者提供任何返回值。

無論采用何種方式退出,在方法退出后都必須返回到最初方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行,方法返回是需要棧幀中保存一些信息,用來幫助恢復它的上層調(diào)用者的執(zhí)行狀態(tài)。

  1. 正常退出時,調(diào)用者的程序計數(shù)器的值可以作為返回地址。
  2. 異常退出時,返回地址通過異常處理器表確定,棧幀中不存。
附加信息

《Java虛擬機規(guī)范》允許虛擬機實現(xiàn)增加一些規(guī)范里沒有描述的信息到棧幀之中,例如與調(diào)試、 性能收集相關的信息,這部分信息完全取決于具體的虛擬機實現(xiàn)。

棧運行原理
  • 在一個活動的線程中,一個時間點上,只有一個活動的棧幀,即只有當前正在執(zhí)行的方法的棧幀(棧頂棧幀)是有效的,這個棧幀被稱為當前棧幀,與當前棧幀對應的方法就是當前方法,定義這個方法的類就是當前類。
  • 執(zhí)行引擎運行的所有字節(jié)碼指令只針對當前棧幀進行操作。
  • 如果在當前方法中調(diào)用了其他方法,則會創(chuàng)建新的棧幀,放在棧頂,成為新的當前棧幀,在調(diào)用的方法返回時,新的當前棧幀會回傳次方法的執(zhí)行結果給前一個棧幀,然后虛擬機會丟棄當前棧幀,使前一個棧幀重新成為當前棧幀。
  • 不同線程中所包含的棧幀不可以相互引用。
  • 方法執(zhí)行后,無論是正常return,還是拋出異常,棧幀都會被彈出。
本地方法棧
  • 本地方法棧(Native M ethod Stacks)與虛擬機棧所發(fā)揮的作用是非常相似的,其區(qū)別只是虛擬機 棧為虛擬機執(zhí)行Java方法(也就是字節(jié)碼)服務,而本地方法棧則是為虛擬機使用到的本地(Native) 方法服務。
  • 常用的HotSpot虛擬機把本地方法棧和虛擬機棧合二為一。與虛擬機棧一樣,本地方法棧也會在棧深度溢出或者棧擴展失 敗時分別拋出StackOverflowError和OutOfMemoryError異常。
  • 本地方法是C語言實現(xiàn)的,具體做法是在Native Method Stack中登記native方法,在執(zhí)行引擎執(zhí)行時加載本地方法庫。
    在這里插入圖片描述

說到堆就很難繞開對象創(chuàng)建,垃圾回收等內(nèi)容,但是本小節(jié)不涉及垃圾回收的內(nèi)容,后續(xù)會單獨開篇講解。

分代思想

先來個全局圖。
在這里插入圖片描述

  • 堆進一步細分,可以劃分為新生代和老年代,其中新生代又可以劃分為Eden空間、Survivor0空間和Survivor1空間。
  • 空間占比默認配置-XX:NewRatio=2,新生代和老年代堆空間占比為1:2,新生代占整個堆空間的1/3,可以修改-XX:NewRatio來改變新生代占比,但是開發(fā)中次參數(shù)一般不會去更改。
  • 在HotSpot中,Eden和S0、S1的空間占比默認是8:1:1,可以通過-XX:SurvivorRatio來調(diào)整空間占比。
  • 分代的目的是優(yōu)化GC性能,沒有分代,那所有的對象都在一起,GC時就會掃描堆的所有區(qū)域。有了分代GC時就會先把"朝生夕死"的新生代區(qū)域進行回收,研究表明在新生代中,對常規(guī)應用進行一次垃圾收集通常 可以回收70%至99%的內(nèi)存空間。
設置堆內(nèi)存的參數(shù)
  • 通過-Xms(-XX:InitialHeapSize)設置堆初始大小,默認值為物理內(nèi)存的1/64。
  • 通過-Xmx(-XX:MaxHeapSize)設置堆大內(nèi)存,默認值為物理內(nèi)存的1/4,堆中內(nèi)存使用超過-Xmx設置的大值,就會拋出OutOfMemoryError異常。
  • 通常會將-Xmx、-Xms配置相同的值,目的是為了在垃圾回收機制清理完堆區(qū)后不需要重新分隔計算堆區(qū)的大小,從而提高效率。
  • 更多參數(shù)配置請參考官方文檔https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
內(nèi)存分配策略
  • 對象優(yōu)先在Eden分配。
  • 大對象直接進入老年代。
    • HotSpot虛擬機提供了-XX:PretenureSizeThreshold 參數(shù),指定大于該設置值的對象直接在老年代分配,這樣做的目的就是避免在Eden區(qū)及兩個Survivor區(qū) 之間來回復制,產(chǎn)生大量的內(nèi)存復制操作。
  • 長期存活的對象將進入老年代。
  • 動態(tài)年齡判斷。
    • HotSpot虛擬機并不是永遠要求對象的年齡必須達到- XX:M axTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于 Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到-XX:
      MaxTenuringThreshold中要求的年齡。
  • 空間分配擔保。
對象在分代空間中流轉

對象通常在Eden區(qū)里誕生,如果經(jīng)過第一次新生代 GC后仍然存活,并且能被Survivor容納的話,該對象會被移動到Survivor空間中,并且將其對象 年齡設為1歲。對象在Survivor區(qū)中每熬過一次Minor GC,就在兩個survivor(S0、S1)中完成一次移動,年齡增加1歲,當它的年齡增加到一定程度(默認為15),就會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數(shù)-XX:
M axTenuringThreshold設置。
在這里插入圖片描述

方法區(qū)(元空間) 概述
  • 方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載 的類型信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù)。雖然《Java虛擬機規(guī)范》中把 方法區(qū)描述為堆的一個邏輯部分,但是它卻有一個別名叫作“非堆”(Non-Heap),目的是與Java堆區(qū) 分開來。
  • 在JDK7及之前版本,習慣把方法區(qū)稱為永久代,JDK8開始使用元空間取代了永久代,本質(zhì)上方法區(qū)和永久代并不等價。
  • 元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。元空間和永久代大的區(qū)別在于,元空間使用的本地內(nèi)存,不在虛擬機設置的內(nèi)存中。
    在這里插入圖片描述
設置元空間內(nèi)存大小
  • 通過-XX:MetaspaceSize設置元空間的初始大小,通過-XX:MaxMetaspaceSize設置元空間大值,64位服務端JVM中MetaspaceSize默認值為21M。這就是初始的高水位線,一旦觸及這個水位線,F(xiàn)ull GC將會被觸發(fā)并卸載沒有用的類,然后這個高水位線將會被重置。新的高水位線值取決于GC后釋放了多少元空間。
運行時常量池
  • 運行時常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本、字 段、方法、接口等描述信息外,還有一項信息是常量池表(Constant Pool Table),用于存放編譯期生 成的各種字面量與符號引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運行時常量池中。
虛擬機棧、堆、方法區(qū)交互

在這里插入圖片描述

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


當前文章:JVM運行時數(shù)據(jù)區(qū)-創(chuàng)新互聯(lián)
文章出自:http://weahome.cn/article/djjocs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部