欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

新手入門Jvm-- JVM對(duì)象創(chuàng)建與內(nèi)存分配機(jī)制

 更新時(shí)間:2021年06月18日 16:56:36   作者:讓你跑39m哈哈  
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的

1. 對(duì)象的創(chuàng)建

對(duì)象創(chuàng)建的主要流程:

1.類加載檢查

虛擬機(jī)遇到一條new指令時(shí),首先將去檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執(zhí)行相應(yīng)的類加載過程。 new指令對(duì)應(yīng)到語言層面上講是,new關(guān)鍵詞、對(duì)象克隆、對(duì)象序列化等。

2.分配內(nèi)存

在類加載檢查通過后,接下來虛擬機(jī)將為新生對(duì)象分配內(nèi)存。對(duì)象所需內(nèi)存的大小在類 加載完成后便可完全確定,為對(duì)象分配空間的任務(wù)等同于把 一塊確定大小的內(nèi)存從Java堆中劃分出來。

這個(gè)步驟有兩個(gè)問題:

  • 如何劃分內(nèi)存。
  • 在并發(fā)情況下, 可能出現(xiàn)正在給對(duì)象A分配內(nèi)存,指針還沒來得及修改, 對(duì)象B又同時(shí)使用了原來的指針來分配內(nèi)存的情況。
  • 劃分內(nèi)存的方法:
    • “指針碰撞”(Bump the Pointer)(默認(rèn)用指針碰撞)

如果Java堆中內(nèi)存是絕對(duì)規(guī)整的,所有用過的內(nèi)存都放在一邊,空閑的內(nèi)存放在另一邊,中間放著一個(gè)指針作為分界點(diǎn)的指示器,那所分配內(nèi)存就僅僅是把那個(gè)指針向空閑空間那邊挪動(dòng)一段與對(duì)象大小相等的距離。

- “空閑列表”(Free List)

如果Java堆中的內(nèi)存并不是規(guī)整的,已使用的內(nèi)存和空 閑的內(nèi)存相互交錯(cuò),那就沒有辦法簡(jiǎn)單地進(jìn)行指針碰撞了,虛擬機(jī)就必須維護(hù)一個(gè)列表,記 錄上哪些內(nèi)存塊是可用的,在分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例, 并更新列表上的記錄

  • 解決并發(fā)問題的方法:
    • CAS(compare and swap)

虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性來對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理。

- 本地線程分配緩沖(Thread Local Allocation Buffer,TLAB)

把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間之中進(jìn)行,即每個(gè)線程在Java堆中預(yù)先分配一小塊內(nèi)存。通過-XX:+/-UseTLAB參數(shù)來設(shè)定虛擬機(jī)是否使用TLAB(JVM會(huì)默認(rèn)開啟-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。

3.初始化零值

內(nèi)存分配完成后,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零值(不包括對(duì)象頭), 如果使用TLAB,這一工作過程也可以提前至TLAB分配時(shí)進(jìn)行。這一步操作保證了對(duì)象的實(shí)例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數(shù)據(jù)類型所對(duì)應(yīng)的零值。

4.設(shè)置對(duì)象頭

初始化零值之后,虛擬機(jī)要對(duì)對(duì)象進(jìn)行必要的設(shè)置,例如這個(gè)對(duì)象是哪個(gè)類的實(shí)例、如何才能找到類的元數(shù)據(jù)信息、對(duì)象的哈希碼、對(duì)象的GC分代年齡等信息。這些信息存放在對(duì)象的對(duì)象頭Object Header之中。

在HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭(Header)、 實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding)。 HotSpot虛擬機(jī)的對(duì)象頭包括兩部分信息,第一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù), 如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時(shí) 間戳等。對(duì)象頭的另外一部分是類型指針,即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。

5.執(zhí)行方法

執(zhí)行方法,即對(duì)象按照程序員的意愿進(jìn)行初始化。對(duì)應(yīng)到語言層面上講,就是為屬性賦值(注意,這與上面的賦零值不同,這是由程序員賦的值),和執(zhí)行構(gòu)造方法。

2. 對(duì)象內(nèi)存分配

對(duì)象內(nèi)存分配流程圖

2.1 對(duì)象棧上分配

我們通過JVM內(nèi)存分配可以知道JAVA中的對(duì)象都是在堆上進(jìn)行分配,當(dāng)對(duì)象沒有被引用的時(shí)候,需要依靠GC進(jìn)行回收內(nèi)存,如果對(duì)象數(shù)量較多的時(shí)候,會(huì)給GC帶來較大壓力,也間接影響了應(yīng)用的性能。為了減少臨時(shí)對(duì)象在堆內(nèi)分配的數(shù)量,JVM通過逃逸分析確定該對(duì)象不會(huì)被外部訪問。如果不會(huì)逃逸可以將該對(duì)象在棧上分配內(nèi)存,這樣該對(duì)象所占用的內(nèi)存空間就可以隨棧幀出棧而銷毀,就減輕了垃圾回收的壓力。

  • 對(duì)象逃逸分析:就是分析對(duì)象動(dòng)態(tài)作用域,當(dāng)一個(gè)對(duì)象在方法中被定義后,它可能被外部方法所引用,例如作為調(diào)用參數(shù)傳遞到其他地方中。

2.2 對(duì)象在Eden區(qū)分配

大多數(shù)情況下,對(duì)象在新生代中 Eden 區(qū)分配。當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次Minor GC。

  • Minor GC/Young GC:指發(fā)生新生代的的垃圾收集動(dòng)作,Minor GC非常頻繁,回收速度一般也比較快。
  • Major GC/Full GC:一般會(huì)回收老年代 ,年輕代,方法區(qū)的垃圾,Major GC的速度一般會(huì)比Minor GC的慢10倍以上。
  • Eden與Survivor區(qū)默認(rèn)8:1:1

大量的對(duì)象被分配在eden區(qū),eden區(qū)滿了后會(huì)觸發(fā)minor gc,可能會(huì)有99%以上的對(duì)象成為垃圾被回收掉,剩余存活的對(duì)象會(huì)被挪到為空的那塊survivor區(qū),下一次eden區(qū)滿了后又會(huì)觸發(fā)minor gc,把eden區(qū)和survivor區(qū)垃圾對(duì)象回收,把剩余存活的對(duì)象一次性挪動(dòng)到另外一塊為空的survivor區(qū),因?yàn)樾律膶?duì)象都是朝生夕死的,存活時(shí)間很短,所以JVM默認(rèn)的8:1:1的比例是很合適的,讓eden區(qū)盡量的大,survivor區(qū)夠用即可,

== JVM默認(rèn)有這個(gè)參數(shù)-XX:+UseAdaptiveSizePolicy(默認(rèn)開啟),會(huì)導(dǎo)致這個(gè)8:1:1比例自動(dòng)變化,如果不想這個(gè)比例有變化可以設(shè)置參數(shù)-XX:-UseAdaptiveSizePolicy ==

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

大對(duì)象就是需要大量連續(xù)內(nèi)存空間的對(duì)象(比如:字符串、數(shù)組)。JVM參數(shù) -XX:PretenureSizeThreshold 可以設(shè)置大對(duì)象的大小,如果對(duì)象超過設(shè)置大小會(huì)直接進(jìn)入老年代,不會(huì)進(jìn)入年輕代,這個(gè)參數(shù)只在 Serial 和ParNew兩個(gè)收集器下有效。比如設(shè)置JVM參數(shù):-XX:PretenureSizeThreshold=1000000 (單位是字節(jié)) -XX:+UseSerialGC ,再執(zhí)行下上面的第一個(gè)程序會(huì)發(fā)現(xiàn)大對(duì)象直接進(jìn)了老年代

2.3.1 為什么要這樣呢?

為了避免為大對(duì)象分配內(nèi)存時(shí)的復(fù)制操作而降低效率。

2.4 長期存活的對(duì)象將進(jìn)入老年代

既然虛擬機(jī)采用了分代收集的思想來管理內(nèi)存,那么內(nèi)存回收時(shí)就必須能識(shí)別哪些對(duì)象應(yīng)放在新生代,哪些對(duì)象應(yīng)放在老年代中。為了做到這一點(diǎn),虛擬機(jī)給每個(gè)對(duì)象一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器。如果對(duì)象在 Eden 出生并經(jīng)過第一次 Minor GC 后仍然能夠存活,并且能被 Survivor 容納的話,將被移動(dòng)到 Survivor 空間中,并將對(duì)象年齡設(shè)為1。對(duì)象在 Survivor 中每熬過一次 MinorGC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為15歲,CMS收集器默認(rèn)6歲,不同的垃圾收集器會(huì)略微有點(diǎn)不同),就會(huì)被晉升到老年代中。對(duì)象晉升到老年代的年齡閾值,可以通過參數(shù) -XX:MaxTenuringThreshold 來設(shè)置。

2.5 對(duì)象動(dòng)態(tài)年齡判斷

當(dāng)前放對(duì)象的Survivor區(qū)域里(其中一塊區(qū)域,放對(duì)象的那塊s區(qū)),一批對(duì)象的總大小大于這塊Survivor區(qū)域內(nèi)存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此時(shí)大于等于這批對(duì)象年齡最大值的對(duì)象,就可以直接進(jìn)入老年代了,例如Survivor區(qū)域里現(xiàn)在有一批對(duì)象,年齡1+年齡2+年齡n的多個(gè)年齡對(duì)象總和超過了Survivor區(qū)域的50%,此時(shí)就會(huì)把年齡n(含)以上的對(duì)象都放入老年代。這個(gè)規(guī)則其實(shí)是希望那些可能是長期存活的對(duì)象,盡早進(jìn)入老年代。對(duì)象動(dòng)態(tài)年齡判斷機(jī)制一般是在minor gc之后觸發(fā)的。

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

年輕代每次minor gc之前JVM都會(huì)計(jì)算下老年代剩余可用空間

如果這個(gè)可用空間小于年輕代里現(xiàn)有的所有對(duì)象大小之和(包括垃圾對(duì)象) 就會(huì)看一個(gè)“-XX:-HandlePromotionFailure”(jdk1.8默認(rèn)就設(shè)置了)的參數(shù)是否設(shè)置了

如果有這個(gè)參數(shù),就會(huì)看看老年代的可用內(nèi)存大小,是否大于之前每一次minor gc后進(jìn)入老年代的對(duì)象的平均大小。

如果上一步結(jié)果是小于或者之前說的參數(shù)沒有設(shè)置,那么就會(huì)觸發(fā)一次Full gc,對(duì)老年代和年輕代一起回收一次垃圾,如果回收完還是沒有足夠空間存放新的對(duì)象就會(huì)發(fā)生"OOM"

當(dāng)然,如果minor gc之后剩余存活的需要挪動(dòng)到老年代的對(duì)象大小還是大于老年代可用空間,那么也會(huì)觸發(fā)full gc,full gc完之后如果還是沒有空間放minor gc之后的存活對(duì)象,則也會(huì)發(fā)生“OOM”

3. 總結(jié)

new Obkect() 在Jvm層面發(fā)生了很多操作,而對(duì)象內(nèi)存分配落在那塊內(nèi)存區(qū)域也有一系列機(jī)制,請(qǐng)大家多多關(guān)注腳本之家其他內(nèi)容!

相關(guān)文章

  • 詳解Java的位運(yùn)算

    詳解Java的位運(yùn)算

    這篇文章主要介紹了詳解Java的位運(yùn)算,程序中的所有數(shù)在計(jì)算機(jī)內(nèi)存中都是以二進(jìn)制的形式儲(chǔ)存的。位運(yùn)算就是直接對(duì)整數(shù)在內(nèi)存中的二進(jìn)制位進(jìn)行操作,需要的朋友可以參考下
    2023-04-04
  • Mybatis使用useGeneratedKeys獲取自增主鍵的方法

    Mybatis使用useGeneratedKeys獲取自增主鍵的方法

    這篇文章主要給大家介紹了關(guān)于Mybatis使用useGeneratedKeys獲取自增主鍵的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Mybatis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • SpringMVC中使用bean來接收form表單提交的參數(shù)時(shí)的注意點(diǎn)

    SpringMVC中使用bean來接收form表單提交的參數(shù)時(shí)的注意點(diǎn)

    本篇文章主要介紹了SpringMVC中使用bean來接收form表單提交的參數(shù)時(shí)的注意點(diǎn),具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-05-05
  • java開發(fā)微信公眾號(hào)支付

    java開發(fā)微信公眾號(hào)支付

    這篇文章主要給大家結(jié)合微信支付接口開發(fā)的實(shí)踐,從獲取用戶授權(quán)到各主要接口的使用方法等方面介紹微信支付的關(guān)鍵點(diǎn)技術(shù),有需要的小伙伴可以參考下
    2015-08-08
  • 詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn)

    詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn)

    這篇文章主要介紹了詳解Java Fibonacci Search斐波那契搜索算法代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Java 獲取當(dāng)前系統(tǒng)時(shí)間的三種方法

    Java 獲取當(dāng)前系統(tǒng)時(shí)間的三種方法

    這篇文章主要介紹了Java 獲取當(dāng)前系統(tǒng)時(shí)間的三種方法,幫助大家利用Java處理時(shí)間,感興趣的朋友可以了解下
    2020-10-10
  • Java 延遲隊(duì)列的常用的實(shí)現(xiàn)方式

    Java 延遲隊(duì)列的常用的實(shí)現(xiàn)方式

    這篇文章主要介紹了Java 延遲隊(duì)列的常用的實(shí)現(xiàn)方式,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-04-04
  • Java eclipse doc文檔生成流程解析

    Java eclipse doc文檔生成流程解析

    這篇文章主要介紹了Java eclipse doc文檔生成流程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java中Jackson的多態(tài)反序列化詳解

    Java中Jackson的多態(tài)反序列化詳解

    這篇文章主要介紹了Java中Jackson的多態(tài)反序列化詳解,多態(tài)序列化與反序列化,主要是借助于Jackson的@JsonTypeInfo與@JsonSubTypes注解實(shí)現(xiàn),下面將通過幾個(gè)例子來簡(jiǎn)述其運(yùn)用,需要的朋友可以參考下
    2023-11-11
  • Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究

    Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究

    這篇文章主要為大家介紹了Java CompletableFuture 異步超時(shí)實(shí)現(xiàn)深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02

最新評(píng)論