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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

JVM 内存模型与内存分配方式

發(fā)布時(shí)間:2025/3/12 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM 内存模型与内存分配方式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • JVM 內(nèi)存模型概述
    • 基于分代收集理論設(shè)計(jì)的垃圾收集器所管理的堆結(jié)構(gòu)
    • 方法區(qū)的演變
  • 內(nèi)存分配
    • 劃分內(nèi)存的方法
    • 劃分內(nèi)存時(shí)如何解決并發(fā)問(wèn)題
    • 對(duì)象棧上分配
    • 基于分代收集理論的垃圾收集器管理下的內(nèi)存分配規(guī)則
      • 對(duì)象優(yōu)先分配在 Eden 區(qū)
      • 大對(duì)象直接進(jìn)入老年代
      • 長(zhǎng)期存活的對(duì)象將逐步進(jìn)入老年代
      • 對(duì)象動(dòng)態(tài)年齡判斷機(jī)制
      • 老年代空間分配擔(dān)保機(jī)制

JVM 內(nèi)存模型概述

JVM 內(nèi)存模型,也叫 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)。下面是 JVM 內(nèi)存模型的圖解:

可以看出,JVM 內(nèi)存模型主要分為以下的幾塊:

  • 堆:線程共享,用于存放對(duì)象實(shí)例,是由垃圾收集器管理的區(qū)域,不同的垃圾收集器對(duì)于堆會(huì)有不同的布局實(shí)現(xiàn)
  • 方法區(qū):線程共享,用于存儲(chǔ)已被加載的所有類的類型信息靜態(tài)變量字段信息方法信息字面量符號(hào)引用等數(shù)據(jù)
  • 程序計(jì)數(shù)器:線程私有,是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,是唯一一塊不會(huì)發(fā)生 OOM 的區(qū)域
  • Native 棧:線程私有,也叫本地方法棧,當(dāng) JVM 執(zhí)行 Native 方法時(shí),存儲(chǔ)一些必要的數(shù)據(jù)
  • JVM 棧:線程私有,也叫虛擬機(jī)棧,每個(gè)方法被執(zhí)行時(shí),JVM 都會(huì)創(chuàng)建一個(gè)棧幀用于存放方法的局部變量表操作數(shù)棧方法返回地址動(dòng)態(tài)鏈接等數(shù)據(jù)

基于分代收集理論設(shè)計(jì)的垃圾收集器所管理的堆結(jié)構(gòu)

堆是由垃圾收集器管理的內(nèi)存區(qū)域,不同的垃圾收集器對(duì)于堆會(huì)有不同的布局實(shí)現(xiàn),這里主要介紹基于分代收集理論設(shè)計(jì)的垃圾收集器所管理的堆結(jié)構(gòu)。
基于分代收集理論,堆內(nèi)存結(jié)構(gòu)如下所示:

可以看出,堆內(nèi)存結(jié)構(gòu)主要分為:

  • Yound Gen:新生代,約占整個(gè)堆大小的 1/3
    • Eden 區(qū):Eden 區(qū),大約占新生代的 8/10
    • Survivor 0 區(qū):S0 區(qū),大約占新生代的 1/10
    • Survivor 1 區(qū):S1 區(qū),大約占新生代的 1/10
  • Old Gen:老年代,約占整個(gè)堆大小的 2/3

方法區(qū)的演變

方法區(qū),在 JDK8 之前,是用永久代來(lái)實(shí)現(xiàn)的,在 JDK8 之后,是用元空間來(lái)實(shí)現(xiàn)的

內(nèi)存分配

劃分內(nèi)存的方法

JVM 劃分內(nèi)存的方法有兩種:

  • 指針碰撞:Bump the Pointer,當(dāng)堆中的內(nèi)存比較整齊,即用過(guò)的內(nèi)存和空閑內(nèi)存有一條清晰的分界線(分界線處有個(gè)指針作為分界點(diǎn)指示器)時(shí),可以使用這種方法
    • 指針碰撞方法在分配內(nèi)存時(shí),僅僅需要將分界點(diǎn)指示器向空閑內(nèi)存方向挪動(dòng)一段距離,距離取決于所需內(nèi)存大小
  • 空閑列表:Free List,當(dāng)隊(duì)中的內(nèi)存不整齊,即用過(guò)的內(nèi)存和空閑內(nèi)存相互交錯(cuò)、沒(méi)有清晰的分界線時(shí),就不能使用指針碰撞的方式來(lái)分配內(nèi)存了。JVM 會(huì)維護(hù)一個(gè)列表,記錄隊(duì)中每塊內(nèi)存的使用情況,在分配的時(shí)候從可用的內(nèi)存中分配一塊足夠大的內(nèi)存出去,并把這塊內(nèi)存標(biāo)記為已使用

劃分內(nèi)存時(shí)如何解決并發(fā)問(wèn)題

在實(shí)際的 JVM 運(yùn)行過(guò)程中,很可能會(huì)出現(xiàn)多個(gè)線程同時(shí)申請(qǐng)內(nèi)存的情況,此時(shí)如果不對(duì)劃分內(nèi)存操作進(jìn)行并發(fā)控制操作,大概率會(huì)出現(xiàn)并發(fā)安全問(wèn)題(多個(gè)內(nèi)存分配請(qǐng)求被分配到了同一塊內(nèi)存空閑)。
針對(duì)上述情況,JVM 采取的并發(fā)控制手段有:

  • CAS:在劃分內(nèi)存時(shí),采用 CAS + 自旋操作來(lái)保證同一塊內(nèi)存不會(huì)同時(shí)被分配給多個(gè)分配請(qǐng)求
  • TLAB:Thread Local Allocation Buffer,即本地線程分配緩沖。核心思想就是把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間中進(jìn)行,即每個(gè)線程在堆中預(yù)先分配一小塊內(nèi)存,這個(gè)線程后續(xù)的內(nèi)存分配請(qǐng)求都會(huì)先在這塊區(qū)域上進(jìn)行,這一小塊內(nèi)存就稱為本地線程分配緩沖。可以通過(guò)設(shè)置參數(shù)來(lái)開(kāi)啟或關(guān)閉此功能。

對(duì)象棧上分配

為了減少生命周期較短的對(duì)象在堆內(nèi)分配的次數(shù),JVM 會(huì)通過(guò)逃逸分析結(jié)合標(biāo)量替換兩個(gè)功能,把一些引用不會(huì)逃逸出方法之外的,可以使用若干個(gè)標(biāo)量來(lái)替代本身的對(duì)象,將其內(nèi)存分配在棧上進(jìn)行。即:

  • 應(yīng)用不會(huì)逃逸出方法之外,即這個(gè)對(duì)象是當(dāng)前方法棧幀中創(chuàng)建出來(lái)的一個(gè)局部變量,其生命周期隨著方法的結(jié)束而結(jié)束
  • 可以使用若干個(gè)標(biāo)量來(lái)替代本身,即這個(gè)對(duì)象可以被分解成若干個(gè)不可再分的標(biāo)量(如基本數(shù)據(jù)類型引用類型等)來(lái)替代

滿足上述兩個(gè)條件的對(duì)象,將可能會(huì)不被創(chuàng)建,而是直接由分解成若干個(gè)分配在棧上的標(biāo)量替代,如:

class User {private int age;private String name; }public void methodA() {User user = new User();user.age = 10;user.name = "Test";//將 user 數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中//由于 user 對(duì)象的引用沒(méi)有傳遞到外部,且 user 對(duì)象本身可以被一個(gè) int 類型和一個(gè) reference 類型的標(biāo)量替換掉//那么在開(kāi)啟了逃逸分析+標(biāo)量替換時(shí),將會(huì)用棧上分配的兩個(gè)標(biāo)量來(lái)替換掉,而不會(huì)在堆中創(chuàng)建這個(gè)對(duì)象...... }

注意,棧上分配必須依賴逃逸分析標(biāo)量替換兩個(gè)功能才能生效。

基于分代收集理論的垃圾收集器管理下的內(nèi)存分配規(guī)則

基于分代收集理論的垃圾收集器,將堆根據(jù)存放對(duì)象的年齡不同劃分成了不同的區(qū)域。在不同的區(qū)域內(nèi),會(huì)有不同的分配規(guī)則。

對(duì)象優(yōu)先分配在 Eden 區(qū)

在大多數(shù)情況下,對(duì)象將會(huì)優(yōu)先分配在 Eden 區(qū)。當(dāng) Eden 區(qū)沒(méi)有足夠的空間進(jìn)行分配時(shí),將會(huì)嘗試進(jìn)行一次 Young GC。

大對(duì)象直接進(jìn)入老年代

當(dāng)對(duì)象的大小超過(guò)一定的閾值時(shí),對(duì)象將會(huì)直接被分配到老年代

長(zhǎng)期存活的對(duì)象將逐步進(jìn)入老年代

Eden + S0 + S1 共同組成 Young Generation 的設(shè)計(jì),稱為 Appel 式垃圾收集機(jī)制。其主要的特點(diǎn)就是長(zhǎng)期存活的對(duì)象將逐步進(jìn)入老年代

  • 每個(gè)對(duì)象的對(duì)象頭 Mark Word 中都會(huì)記錄一個(gè)當(dāng)前對(duì)象年齡的計(jì)數(shù)器
  • 在每一次經(jīng)歷 Young GC 后,如果對(duì)象依然存活,那么計(jì)數(shù)器將 +1
  • 在經(jīng)歷若干次(默認(rèn)為 15 次)Young GC 還依然存活的對(duì)象,也即對(duì)象年齡大于晉升老年代年齡閾值(默認(rèn)為 15)的對(duì)象,將會(huì)被移動(dòng)到老年代中

對(duì)象動(dòng)態(tài)年齡判斷機(jī)制

在某一次 Young GC 時(shí),如果此時(shí) Survivor 空間中小于等于某年齡的所有對(duì)象大小的總和大于等于 Survivor 空間大小的一半,那么大于或等于該年齡的所有存活對(duì)象將直接進(jìn)入到老年代中(盡管此時(shí)對(duì)象年齡可能尚未達(dá)到晉升老年代年齡閾值

老年代空間分配擔(dān)保機(jī)制

Appel 式垃圾收集機(jī)制,核心思想使用的是復(fù)制算法。但是這個(gè)復(fù)制算法的備用空間(空閑的 Survivor 區(qū))遠(yuǎn)小于主用空間(Eden 區(qū) + 使用中的 Survivor 區(qū)),那么如果在某次 Young GC 時(shí),存活下來(lái)的對(duì)象比備用空間大怎么辦?
Appel 式垃圾收集機(jī)制給出的解決方案就是將這些所有存活下來(lái)的對(duì)象都移動(dòng)到老年代中。這就是老年代空間分配擔(dān)保機(jī)制的來(lái)源以及核心思想。
更細(xì)節(jié)的:

  • 在發(fā)生 Young GC 之前,JVM 必須檢查當(dāng)前老年代最大可用的連續(xù)空間是否大于新生代當(dāng)前所有對(duì)象的總空間
    • 如果成立,那么可以安全地執(zhí)行本次 Young GC
    • 如果不成立,那么 JVM 會(huì)查看當(dāng)前是否開(kāi)啟了老年代空間分配擔(dān)保機(jī)制(JDK 8 之后默認(rèn)開(kāi)啟)
      • 如果未開(kāi)啟,那么將會(huì)執(zhí)行一次 Full GC
      • 如果開(kāi)啟了,那么將會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次 Young GC 晉升到老年代的對(duì)象的平均總大小
        • 如果小于,那么將會(huì)執(zhí)行一次 Full GC
        • 如果大于,那么將會(huì)有風(fēng)險(xiǎn)地執(zhí)行本次 Young GC

注意,當(dāng)有風(fēng)險(xiǎn)地執(zhí)行 Young GC 時(shí),如果本次 Young GC 存活下來(lái)的對(duì)象總大小大于老年代最大可用的連續(xù)空間(即出現(xiàn)了擔(dān)保失敗的情況),那么將會(huì)執(zhí)行一次 Full GC

總結(jié)

以上是生活随笔為你收集整理的JVM 内存模型与内存分配方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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