<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          玩轉(zhuǎn) JVM 中的對(duì)象及引用

          共 5557字,需瀏覽 12分鐘

           ·

          2023-05-26 17:23

          玩轉(zhuǎn) JVM 中的對(duì)象及引用

          • 前言

          • 一、JVM 中對(duì)象的創(chuàng)建過程

            • 1).檢查加載

            • 2).分配內(nèi)存

            • 2、對(duì)象的內(nèi)存分配

            • 3、指針碰撞

            • 4、空閑列表

            • 5、并發(fā)安全

            • 6、CAS 機(jī)制

            • 7、分配緩沖

          • 二、對(duì)象的內(nèi)存布局

            • 1、句柄

            • 2、直接指針

            • 3、判斷對(duì)象的存活

            • 4、引用計(jì)數(shù)法

            • 5、可達(dá)性分析

          • 三、各種引用

            • 1、強(qiáng)引用

            • 2、軟引用 SoftReference

            • 3、弱引用 WeakReference

            • 4、虛引用 PhantomReference

          • 四、對(duì)象的分配策略

            • 1、棧上分配

            • 2、逃逸分析

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

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

            • 五、長期存活對(duì)象進(jìn)入老年區(qū)

            • 六、對(duì)象年齡動(dòng)態(tài)判定

            • 七、空間分配擔(dān)保

          • 總結(jié)


          前言

          提示:這里可以添加本文要記錄的大概內(nèi)容:
          例如:隨著人工智能的不斷發(fā)展,機(jī)器學(xué)習(xí)這門技術(shù)也越來越重要,很多人都開啟了學(xué)習(xí)機(jī)器學(xué)習(xí),本文就介紹了機(jī)器學(xué)習(xí)的基礎(chǔ)內(nèi)容。

          提示:以下是本篇文章正文內(nèi)容,下面案例可供參考

          一、JVM 中對(duì)象的創(chuàng)建過程

          1f7d7e119de526b06505f0bc50379ee7.webp

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

          虛擬機(jī)遇到一條 new 指令時(shí),首先檢查是否被類加載器加載,如果沒有,那必須先執(zhí)行相應(yīng)的類加載過程。類加載就是把 class 加載到 JVM 的運(yùn)行時(shí)數(shù)據(jù)區(qū)的過程

          1).檢查加載

          首先檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類的符號(hào)引用(符號(hào)引用 :符號(hào)引用以一組符號(hào)來描述所引用的目標(biāo)),并且檢查類是否已經(jīng)被加載、 解析和初始化過。

          2).分配內(nèi)存

          接下來虛擬機(jī)將為新生對(duì)象分配內(nèi)存。為對(duì)象分配空間的任務(wù)等同于把一塊確定大小的內(nèi)存從 Java 堆中劃分出來。

          3、指針碰撞

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

          4、空閑列表

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

          5、并發(fā)安全

          除如何劃分可用空間之外,還有另外一個(gè)需要考慮的問題是對(duì)象創(chuàng)建在虛擬機(jī)中是非常頻繁的行為,即使是僅僅修改一個(gè)指針?biāo)赶虻奈恢茫诓l(fā)情 況下也并不是線程安全的,可能出現(xiàn)正在給對(duì)象 A 分配內(nèi)存,指針還沒來得及修改,對(duì)象 B 又同時(shí)使用了原來的指針來分配內(nèi)存的情況。

          6、CAS 機(jī)制

          解決這個(gè)問題有兩種方案,一種是對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理——實(shí)際上虛擬機(jī)采用 CAS 配上失敗重試的方式保證更新操作的原子性

          7、分配緩沖

          另一種是把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間之中進(jìn)行,即每個(gè)線程在 Java 堆中預(yù)先分配一小塊私有內(nèi)存,也就是本地線程分配緩沖(Thread Local Allocation Buffer,TLAB),JVM 在線程初始化時(shí),同時(shí)也會(huì)申請(qǐng)一塊指定大小的內(nèi)存,只給當(dāng)前線程使用,這樣每個(gè)線程都單獨(dú)擁有一個(gè) Buffer,如果 需要分配內(nèi)存,就在自己的 Buffer 上分配,這樣就不存在競爭的情況,可以大大提升分配效率,當(dāng) Buffer 容量不夠的時(shí)候,再重新從 Eden 區(qū)域申請(qǐng)一塊 繼續(xù)使用。TLAB 的目的是在為新對(duì)象分配內(nèi)存空間時(shí),讓每個(gè) Java 應(yīng)用線程能在使用自己專屬的分配指針來分配空間,減少同步開銷。TLAB 只是讓每個(gè)線程有私有的分配指針,但底下存對(duì)象的內(nèi)存空間還是給所有線程訪問的,只是其它線程無法在這個(gè)區(qū)域分配而已。當(dāng)一個(gè) TLAB 用滿(分 配指針 top 撞上分配極限 end 了),就新申請(qǐng)一個(gè) TLAB。參數(shù):
          -XX:+UseTLAB 允許在年輕代空間中使用線程本地分配塊(TLAB)。默認(rèn)情況下啟用此選項(xiàng)。要禁用 TLAB,請(qǐng)指定-XX:-UseTLAB

          二、對(duì)象的內(nèi)存布局

          f5c671cffdef655da4392f92b065ef27.webp
          在 HotSpot 虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為 3 塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding)。對(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í)例。如果對(duì)象是一個(gè) java 數(shù)組,那么在對(duì)象頭中還有一塊用于記錄數(shù)組長度的數(shù)據(jù)。第三部分對(duì)齊填充并不是必然存在的,也沒有特別的含義,它僅僅起著占位符的作用。由于 HotSpot VM 的自動(dòng)內(nèi)存管理系統(tǒng)要求對(duì)對(duì)象的大小必須 是 8 字節(jié)的整數(shù)倍。當(dāng)對(duì)象其他數(shù)據(jù)部分沒有對(duì)齊時(shí),就需要通過對(duì)齊填充來補(bǔ)全。

          1、句柄

          如果使用句柄訪問的話,那么 Java 堆中將會(huì)劃分出一塊內(nèi)存來作為句柄池,reference 中存儲(chǔ)的就是對(duì)象的句柄地址,而句柄中包含了對(duì)象實(shí)例數(shù)據(jù)與類 型數(shù)據(jù)各自的具體地址信息。使用句柄來訪問的最大好處就是 reference 中存儲(chǔ)的是穩(wěn)定的句柄地址,在對(duì)象被移動(dòng)(垃圾收集時(shí)移動(dòng)對(duì)象是非常普遍的行為)時(shí)只會(huì)改變句柄中的實(shí) 例數(shù)據(jù)指針,而 reference 本身不需要修改.

          2、直接指針

          如果使用直接指針訪問, reference 中存儲(chǔ)的直接就是對(duì)象地址。這兩種對(duì)象訪問方式各有優(yōu)勢, 使用直接指針訪問方式的最大好處就是速度更快,它節(jié)省了一次指針定位的時(shí)間開銷,由于對(duì)象的訪問在 Java 中非常頻 繁,因此這類開銷積少成多后也是一項(xiàng)非常可觀的執(zhí)行成本。對(duì) Sun HotSpot 而言,它是使用直接指針訪問方式進(jìn)行對(duì)象訪問的

          3、判斷對(duì)象的存活

          在堆里面存放著幾乎所有的對(duì)象實(shí)例,垃圾回收器在對(duì)對(duì)進(jìn)行回收前,要做的事情就是確定這些對(duì)象中哪些還是“存活”著,哪些已經(jīng)“死去”(死去 代表著不可能再被任何途徑使用得對(duì)象了)

          4、引用計(jì)數(shù)法

          在對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器就加 1,當(dāng)引用失效時(shí),計(jì)數(shù)器減 1

          5、可達(dá)性分析

          來判定對(duì)象是否存活的。這個(gè)算法的基本思路就是通過一系列的稱為“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為 引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的。

          三、各種引用

          1、強(qiáng)引用

          一般的 Object obj = new Object() ,就屬于強(qiáng)引用。在任何情況下,只有有強(qiáng)引用關(guān)聯(lián)(與根可達(dá))還在,垃圾回收器就永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。

          2、軟引用 SoftReference

          空間,才會(huì)拋出內(nèi)存溢出)。參見代碼:VM 參數(shù) -Xms10m -Xmx10m -XX:+PrintGC

          3、弱引用 WeakReference

          一些有用(程度比軟引用更低)但是并非必需,用弱引用關(guān)聯(lián)的對(duì)象,只能生存到下一次垃圾回收之前,GC 發(fā)生時(shí),不管內(nèi)存夠不夠,都會(huì)被回收。

          4、虛引用 PhantomReference

          幽靈引用,最弱(隨時(shí)會(huì)被回收掉) 垃圾回收的時(shí)候收到一個(gè)通知,就是為了監(jiān)控垃圾回收器是否正常工作。

          四、對(duì)象的分配策略

          1、棧上分配

          沒有逃逸(了解即可) 即方法中的對(duì)象沒有發(fā)生逃逸。逃逸分析的原理:分析對(duì)象動(dòng)態(tài)作用域,當(dāng)一個(gè)對(duì)象在方法中定義后,它可能被外部方法所引用。比如:調(diào)用參數(shù)傳遞到其他方法中,這種稱之為方法逃逸。甚至還有可能被外部線程訪問到,例如:賦值給其他線程中訪問的變量,這個(gè)稱之為線程逃逸。從不逃逸到方法逃逸到線程逃逸,稱之為對(duì)象由低到高的不同逃逸程度。如果確定一個(gè)對(duì)象不會(huì)逃逸出線程之外,那么讓對(duì)象在棧上分配內(nèi)存可以提高 JVM 的效率

          2、逃逸分析

          如果是逃逸分析出來的對(duì)象可以在棧上分配的話,那么該對(duì)象的生命周期就跟隨線程了,就不需要垃圾回收,如果是頻繁的調(diào)用此方法則可以得到很大的性能提高。采用了逃逸分析后,滿足逃逸的對(duì)象在棧上分配

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

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

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

          大對(duì)象就是指需要大量連續(xù)內(nèi)存空間的 Java 對(duì)象,最典型的大對(duì)象便是那種很長的字符串,或者元素?cái)?shù)量很龐大的數(shù)組。大對(duì)象對(duì)虛擬機(jī)的內(nèi)存分配來說就是一個(gè)不折不扣的壞消息,比遇到一個(gè)大對(duì)象更加壞的消息就是遇到- -群“朝生夕滅”的“短命大對(duì)象”,我們寫程序 的時(shí)候應(yīng)注意避免。在 Java 虛擬機(jī)中要避免大對(duì)象的原因是,在分配空間時(shí),它容易導(dǎo)致內(nèi)存明明還有不少空間時(shí)就提前觸發(fā)垃圾收集,以獲取足夠的連續(xù)空間才能安置好 它們。而當(dāng)復(fù)制對(duì)象時(shí),大對(duì)象就意味著高額的內(nèi)存復(fù)制開銷。HotSpot 虛擬機(jī)提供了-XX:PretenureSizeThreshold 參數(shù),指定大于該設(shè)置值的對(duì)象直接在老年代分配,這樣做的目的就是避免在 Eden 區(qū)及兩個(gè) Survivor 區(qū)之間來回復(fù)制,產(chǎn)生大量的內(nèi)存復(fù)制操作。這樣做的目的:1.避免大量內(nèi)存復(fù)制,2.避免提前進(jìn)行垃圾回收,明明內(nèi)存有空間進(jìn)行分配。PretenureSizeThreshold 參數(shù)只對(duì) Serial 和 ParNew 兩款收集器有效。-XX:PretenureSizeThreshold=4m

          五、長期存活對(duì)象進(jìn)入老年區(qū)

          HotSpot 虛擬機(jī)中多數(shù)收集器都采用了分代收集來管理堆內(nèi)存,那內(nèi)存回收時(shí)就必須能決策哪些存活對(duì)象應(yīng)當(dāng)放在新生代,哪些存活對(duì)象放在老年代中。為做到這點(diǎn),虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器,存儲(chǔ)在對(duì)象頭中。4096ce945bcc5fe70876b67540745ce9.webp

          六、對(duì)象年齡動(dòng)態(tài)判定

          為了能更好地適應(yīng)不同程序的內(nèi)存狀況,虛擬機(jī)并不是永遠(yuǎn)地要求對(duì)象的年齡必須達(dá)到了 MaxTenuringThreshold 才能晉升老年代,如果在 Survivor 空間中 相同年齡所有對(duì)象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代,無須等到 MaxTenuringThreshold 中要求的 年齡

          七、空間分配擔(dān)保

          在發(fā)生 Minor GC 之前,虛擬機(jī)會(huì)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果這個(gè)條件成立,那么 Minor GC 可以確保是安全 的。如果不成立,則虛擬機(jī)會(huì)查看 HandlePromotionFailure 設(shè)置值是否允許擔(dān)保失敗。如果允許,那么會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷 次晉升到老年代對(duì)象的平均大小,如果大于,將嘗試著進(jìn)行一次 Minor GC,盡管這次 Minor GC 是有風(fēng)險(xiǎn)的,如果擔(dān)保失敗則會(huì)進(jìn)行一次 Full GC;如果小 于,或者 HandlePromotionFailure 設(shè)置不允許冒險(xiǎn),那這時(shí)也要改為進(jìn)行一次 Full GC。

          總結(jié)

          這節(jié)概念性的東西還是比較多的,掃盲階段。


          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  97精品人妻一区二区三区蜜桃 | 欧美一道本在线 | 欧美日韩国产免费电影 | 黄色三级片在线啊不要 | 国产高清无码内射视频在线观看 |