日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

JVM 学习笔记 1. JVM 运行模型

發(fā)布時(shí)間:2025/3/21 编程问答 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM 学习笔记 1. JVM 运行模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • JVM 啟動(dòng)流程
  • JVM 基本結(jié)構(gòu)
  • 內(nèi)存模型
  • 虛擬機(jī)的運(yùn)行方式
  • 1. JVM 啟動(dòng)流程

    如下圖所示:

    2. JVM 基本結(jié)構(gòu)

    兩幅經(jīng)典的模型圖:

    其中:

  • PC寄存器:每個(gè)線程都擁有一個(gè)PC寄存器,用于指向下一條指令的地址,因此,PC是線程私有的內(nèi)存。當(dāng)執(zhí)行 native 方法時(shí),PC的值為undefined。此內(nèi)存區(qū)域是唯一一個(gè)在 Java 虛擬機(jī)規(guī)范中沒有規(guī)定 OOM 的區(qū)域。
  • Java 棧:是線程私有的,棧是由一系列幀(frame)組成。JVM 是 Stack-Based 的,棧幀中保存:方法的返回值(Return Value),局部變量表(Local variables),操作數(shù)棧(Operand Stack)和常量池指針(Constant Pool Refernce)。其中,局部變量表包含了參數(shù)和局部變量(槽)。兩個(gè)經(jīng)典的異常:StackOverflowError 和 OutOfMemoryError。
  • 本地方法棧:JVM 執(zhí)行 Native 方法所使用的棧,HotSpot 直接將 Java 棧和 Native 棧合二為一。
  • Java 堆:用于保存應(yīng)用程序?qū)ο髮?shí)例,被所有線程所共享,是發(fā)生 GC 的主要區(qū)域。對(duì)于分代 GC 而言,堆也是分代的:新生代和老年代。Java 堆可能劃分出多個(gè)線程私有的分配緩存區(qū)(TLAB, Thread Local Allocation Buffer)。
  • 方法區(qū):被各個(gè)線程共享。用來(lái)存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、方法的字節(jié)碼等數(shù)據(jù)。別名是 Non-Heap,在 HotSpot 中,方法區(qū)經(jīng)常被稱為永久代(PermG),因?yàn)镠otSpot將分代延生到方法區(qū),該區(qū)域也可盡心回收。值得注意的是,JDK1.7的HotSpot已經(jīng)將Interned Strings(字符串常量池)移出永久代。
  • 運(yùn)行時(shí)常量池(Runtime Constant Pool):方法區(qū)的一部分。主要用來(lái)存放編譯期生成的各種字面量和符號(hào)引用,當(dāng)然,運(yùn)行時(shí)也可以將新的常量放入池中,如:String.intern()方法。
  • 直接內(nèi)存(Direct Memory):不是 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,沒有在虛擬機(jī)規(guī)范中定義該內(nèi)存區(qū)域。NIO 利用 Native 函數(shù)庫(kù)直接分配堆外內(nèi)存,避免了Java堆和 堆外內(nèi)存的來(lái)回復(fù)制,提高了性能。
  • 棧的執(zhí)行過程

    JVM 沒有寄存器(除PC),所有的參數(shù)傳遞都使用操作數(shù)棧

    public static int add(int a,int b){int c = 0;c = a + b;return c; }

    編譯之后,注意操作數(shù)棧如何實(shí)現(xiàn)參數(shù)傳遞。

    0: iconst_0 // 0壓棧1: istore_2 // 彈出int,存放于局部變量22: iload_0 // 把局部變量0壓棧3: iload_1 // 局部變量1壓棧4: iadd // 彈出2個(gè)變量,求和,結(jié)果壓棧5: istore_2 // 彈出結(jié)果,放于局部變量26: iload_2 // 局部變量2壓棧7: ireturn // 返回

    局部變量表和操作數(shù)棧的變化過程:

    棧上分配

    public class OnStackTest {public static void alloc() {byte[] b = new byte[2];b[0] = 1;}public static void main(String[] args) {long b = System.currentTimeMillis();for (int i = 0; i < 100000000; i++) {alloc();}long e = System.currentTimeMillis();System.out.println(e - b);} }

    默認(rèn)運(yùn)行,采用了棧上分配了. 測(cè)試結(jié)果:

    ? jvm-learning java com.nil2inf.memory.OnStackTest 52 ? jvm-learning java -server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC com.nil2inf.memory.OnStackTest 40 ? jvm-learning java -server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC com.nil2inf.memory.OnStackTest [GC 2624K->432K(9856K), 0.0014784 secs] [GC 3056K->416K(9856K), 0.0006314 secs] [GC 3040K->432K(9856K), 0.0004287 secs] [GC 3056K->416K(9728K), 0.0003950 secs] ... ... [GC 3280K->400K(9984K), 0.0001506 secs] [GC 3280K->400K(10048K), 0.0001993 secs] [GC 3408K->400K(10048K), 0.0001092 secs] ... ... [GC 3664K->400K(10176K), 0.0001128 secs] 582

    棧上分配:

    • 棧上分配、標(biāo)量替換技術(shù)是JVM的一項(xiàng)優(yōu)化技術(shù),涉及到逃逸分析和標(biāo)量替換。
    • 通常只有沒有逃逸的小對(duì)象,才可以棧上分配。反之,大對(duì)象或者逃逸對(duì)象無(wú)法棧上分配。
    • 棧上分配的目的是減清 GC 的壓力。

    3. 內(nèi)存模型

  • 每一個(gè)線程有一個(gè)工作內(nèi)存和主內(nèi)存獨(dú)立。
  • 工作內(nèi)存存放主存中變量的值的拷貝。
  • 原子操作。
    read and load 從主存復(fù)制變量到當(dāng)前工作內(nèi)存
    use and assign 執(zhí)行代碼,改變共享變量值
    store and write 用工作內(nèi)存數(shù)據(jù)刷新主存相關(guān)內(nèi)容
  • 使用 volatile 關(guān)鍵字能夠保證變量更改在其他線程立即可見。
  • 可見性

    一個(gè)線程修改了變量,其他線程可以立即知道。

    如何確保可見性:

    • volatile
    • synchronized (unlock之前,寫變量值回主存)
    • final(一旦初始化完成,其他線程就可見)

    有序性

    在本線程內(nèi),操作都是有序的。在線程外觀察,操作都是無(wú)序的。(指令重排 或 主內(nèi)存同步延時(shí))。

    重排序

    指令重排序。

    4. 虛擬機(jī)的運(yùn)行方式

    虛擬機(jī)中存在兩種運(yùn)行方式:分為解釋和編譯。

    字節(jié)碼指令編譯為本機(jī)機(jī)器指令過程,有解釋器或者編譯器完成.

    a. 解釋
    解釋是最簡(jiǎn)單的字節(jié)碼編譯形式. 解釋器查找每條字節(jié)碼指令對(duì)應(yīng)的硬件編碼,再由 CPU 執(zhí)行相應(yīng)的硬件指令。

    這個(gè)過程可以準(zhǔn)確執(zhí)行字節(jié)碼,沒有機(jī)會(huì)對(duì)某個(gè)指令集合進(jìn)行優(yōu)化,難以發(fā)揮目標(biāo)平臺(tái)處理器的最佳性能。

    b. 編譯
    編譯執(zhí)行應(yīng)用程序時(shí),編譯器會(huì)將加載運(yùn)行時(shí)會(huì)用到的全部代碼. 因?yàn)榫幾g器可以將字節(jié)碼編譯為本地代碼,因此它可以獲取到完整或部分運(yùn)行時(shí)上下文信息,并依據(jù)收集到的信息決定到底應(yīng)該如何編譯字節(jié)碼。

    可以對(duì)指令集合進(jìn)行優(yōu)化,優(yōu)化后的指令集合會(huì)被存儲(chǔ)到 code cache 的數(shù)據(jù)結(jié)構(gòu)中,當(dāng)下次執(zhí)行這部分字節(jié)碼序列時(shí),會(huì)執(zhí)行這些經(jīng)過優(yōu)化后被存儲(chǔ)到code cache的指令集合。在某些情況下,性能計(jì)數(shù)器會(huì)失效,并覆蓋掉先前所做的優(yōu)化,這時(shí),編譯器會(huì)執(zhí)行一次新的優(yōu)化過程。
    使用code cache的好處是優(yōu)化后的指令集可以立即執(zhí)行.

    c. 優(yōu)化
    隨著動(dòng)態(tài)編譯器一起出現(xiàn)的是性能計(jì)數(shù)器。

    例如,編譯器會(huì)插入性能計(jì)數(shù)器,以統(tǒng)計(jì)每個(gè)字節(jié)碼塊(對(duì)應(yīng)與某個(gè)被調(diào)用的方法)的調(diào)用次數(shù)。 -- 代碼的熱度.

    運(yùn)行時(shí)數(shù)據(jù)監(jiān)控有助于編譯器完成多種代碼優(yōu)化工作,進(jìn)一步提升代碼執(zhí)行性能。

    轉(zhuǎn)載于:https://www.cnblogs.com/nil2inf/p/4722276.html

    總結(jié)

    以上是生活随笔為你收集整理的JVM 学习笔记 1. JVM 运行模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。