国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

對象存活判斷

這篇具有很好參考價值的文章主要介紹了對象存活判斷。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

對象存活判斷

在堆里存放著幾乎所有的 Java 對象實例,在 GC 執(zhí)行垃圾回收之前,首先需要區(qū)分出內(nèi)存中哪些是存活對象,哪些是已經(jīng)死亡的對象。只有被標記為己經(jīng)死亡的對象,GC 才會在執(zhí)行垃圾回收時,釋放掉其所占用的內(nèi)存空間,因此這個過程我們可以稱為垃圾標記階段。

那么在 JVM 中究竟是如何標記一個死亡對象呢?簡單來說,當一個對象已經(jīng)不再被任何的存活對象繼續(xù)引用時,就可以宣判為已經(jīng)死亡。

判斷對象存活一般有兩種方式:引用計數(shù)算法可達性分析算法。

標記階段:引用計數(shù)算法

方式一:引用計數(shù)算法

引用計數(shù)算法(Reference Counting)比較簡單,對每個對象保存一個整型的引用計數(shù)器屬性。用于記錄對象被引用的情況。

對于一個對象 A,只要有任何一個對象引用了 A,則 A 的引用計數(shù)器就加 1;當引用失效時,引用計數(shù)器就減 1。只要對象 A 的引用計數(shù)器的值為 0,即表示對象 A 不可能再被使用,可進行回收。

優(yōu)點:實現(xiàn)簡單,垃圾對象便于辨識;判定效率高,回收沒有延遲性。

缺點:

  • 它需要單獨的字段存儲計數(shù)器,這樣的做法增加了存儲空間的開銷。

  • 每次賦值都需要更新計數(shù)器,伴隨著加法和減法操作,這增加了時間開銷。

  • 引用計數(shù)器有一個嚴重的問題,即無法處理循環(huán)引用的情況。這是一條致命缺陷,導致在 Java 的垃圾回收器中沒有使用這類算法。

循環(huán)引用

當 p 的指針斷開的時候,內(nèi)部的引用形成一個循環(huán),這就是循環(huán)引用

對象存活判斷,JVM,jvm

舉例

測試 Java 中是否采用的是引用計數(shù)算法

public class RefCountGC {
 ? ?// 這個成員屬性的唯一作用就是占用一點內(nèi)存
 ? ?private byte[] bigSize = new byte[5*1024*1024];
 ? ?// 引用
 ? ?Object reference = null;
?
 ? ?public static void main(String[] args) {
 ? ? ? ?RefCountGC obj1 = new RefCountGC();
 ? ? ? ?RefCountGC obj2 = new RefCountGC();
?
 ? ? ? ?obj1.reference = obj2;
 ? ? ? ?obj2.reference = obj1;
?
 ? ? ? ?obj1 = null;
 ? ? ? ?obj2 = null;
 ? ? ? ?// 顯示的執(zhí)行垃圾收集行為
 ? ? ? ?// 這里發(fā)生GC,obj1和obj2是否被回收?
 ? ? ? ?System.gc();
 ?  }
}
// 運行結(jié)果
PSYoungGen: 15490K->808K(76288K)] 15490K->816K(251392K)

上述進行了 GC 收集的行為,所以可以證明 JVM 中采用的不是引用計數(shù)器的算法

對象存活判斷,JVM,jvm

小結(jié)

引用計數(shù)算法,是很多語言的資源回收選擇,例如因人工智能而更加火熱的 Python,它更是同時支持引用計數(shù)和垃圾收集機制。

具體哪種最優(yōu)是要看場景的,業(yè)界有大規(guī)模實踐中僅保留引用計數(shù)機制,以提高吞吐量的嘗試。

Java 并沒有選擇引用計數(shù),是因為其存在一個基本的難題,也就是很難處理循環(huán)引用關(guān)系。

Python 如何解決循環(huán)引用?

  • 手動解除:很好理解,就是在合適的時機,解除引用關(guān)系。 使用弱引用 weakref,weakref 是 Python 提供的標準庫,旨在解決循環(huán)引用。

標記階段:可達性分析算法

可達性分析算法(根搜索算法、追蹤性垃圾收集)

相對于引用計數(shù)算法而言,可達性分析算法不僅同樣具備實現(xiàn)簡單和執(zhí)行高效等特點,更重要的是該算法可以有效地解決在引用計數(shù)算法中循環(huán)引用的問題,防止內(nèi)存泄漏的發(fā)生。

相較于引用計數(shù)算法,這里的可達性分析就是 Java、C#選擇的。這種類型的垃圾收集通常也叫作追蹤性垃圾收集(Tracing Garbage Collection)

所謂"GCRoots”根集合就是一組必須活躍的引用。

基本思路
  • 可達性分析算法是以根對象集合(GCRoots)為起始點,按照從上至下的方式搜索被根對象集合所連接的目標對象是否可達。

  • 使用可達性分析算法后,內(nèi)存中的存活對象都會被根對象集合直接或間接連接著,搜索所走過的路徑稱為引用鏈(Reference Chain)

  • 如果目標對象沒有任何引用鏈相連,則是不可達的,就意味著該對象己經(jīng)死亡,可以標記為垃圾對象。

  • 在可達性分析算法中,只有能夠被根對象集合直接或者間接連接的對象才是存活對象。

對象存活判斷,JVM,jvm

?

在 Java 語言中,GC Roots 包括以下幾類元素:

  • 虛擬機棧中引用的對象

    • 比如:各個線程被調(diào)用的方法中使用到的參數(shù)、局部變量等。

  • 本地方法棧內(nèi) JNI(通常說的本地方法)引用的對象

  • 方法區(qū)中類靜態(tài)屬性引用的對象

    • 比如:Java 類的引用類型靜態(tài)變量

  • 方法區(qū)中常量引用的對象

    • 比如:字符串常量池(String Table)里的引用

  • 所有被同步鎖 synchronized 持有的對象

  • Java 虛擬機內(nèi)部的引用。

    • 基本數(shù)據(jù)類型對應的 Class 對象,一些常駐的異常對象(如:NullPointerException、OutOfMemoryError),系統(tǒng)類加載器。

  • 反映 java 虛擬機內(nèi)部情況的 JMXBean、JVMTI 中注冊的回調(diào)、本地代碼緩存等。

對象存活判斷,JVM,jvm

?

除了這些固定的 GC Roots 集合以外,根據(jù)用戶所選用的垃圾收集器以及當前回收的內(nèi)存區(qū)域不同,還可以有其他對象“臨時性”地加入,共同構(gòu)成完整 GC Roots 集合。比如:分代收集和局部回收(PartialGC)。

如果只針對 Java 堆中的某一塊區(qū)域進行垃圾回收(比如:典型的只針對新生代),必須考慮到內(nèi)存區(qū)域是虛擬機自己的實現(xiàn)細節(jié),更不是孤立封閉的,這個區(qū)域的對象完全有可能被其他區(qū)域的對象所引用,這時候就需要一并將關(guān)聯(lián)的區(qū)域?qū)ο笠布尤?GCRoots 集合中去考慮,才能保證可達性分析的準確性。

小技巧:由于 Root 采用棧方式存放變量和指針,所以如果一個指針,它保存了堆內(nèi)存里面的對象,但是自己又不存放在堆內(nèi)存里面,那它就是一個 Root。

注意

如果要使用可達性分析算法來判斷內(nèi)存是否可回收,那么分析工作必須在一個能保障一致性的快照中進行。這點不滿足的話分析結(jié)果的準確性就無法保證。

這點也是導致 GC 進行時必須“stop The World”的一個重要原因。

  • 即使是號稱(幾乎)不會發(fā)生停頓的 CMS 收集器中,枚舉根節(jié)點時也是必須要停頓的。

對象的 finalization 機制

Java 語言提供了對象終止(finalization)機制來允許開發(fā)人員提供對象被銷毀之前的自定義處理邏輯。

當垃圾回收器發(fā)現(xiàn)沒有引用指向一個對象,即:垃圾回收此對象之前,總會先調(diào)用這個對象的 finalize()方法。

finalize() 方法允許在子類中被重寫,用于在對象被回收時進行資源釋放。通常在這個方法中進行一些資源釋放和清理的工作,比如關(guān)閉文件、套接字和數(shù)據(jù)庫連接等。

永遠不要主動調(diào)用某個對象的 finalize()方法 I 應該交給垃圾回收機制調(diào)用。理由包括下面三點:

  • 在 finalize()時可能會導致對象復活。

  • finalize()方法的執(zhí)行時間是沒有保障的,它完全由 GC 線程決定,極端情況下,若不發(fā)生 GC,則 finalize()方法將沒有執(zhí)行機會。

  • 一個糟糕的 finalize()會嚴重影響 Gc 的性能。

從功能上來說,finalize()方法與 C++中的析構(gòu)函數(shù)比較相似,但是 Java 采用的是基于垃圾回收器的自動內(nèi)存管理機制,所以 finalize()方法在本質(zhì)上不同于 C++中的析構(gòu)函數(shù)。

由于 finalize()方法的存在,虛擬機中的對象一般處于三種可能的狀態(tài)。

生存還是死亡?

如果從所有的根節(jié)點都無法訪問到某個對象,說明對象己經(jīng)不再使用了。一般來說,此對象需要被回收。但事實上,也并非是“非死不可”的,這時候它們暫時處于“緩刑”階段。一個無法觸及的對象有可能在某一個條件下“復活”自己,如果這樣,那么對它的回收就是不合理的,為此,定義虛擬機中的對象可能的三種狀態(tài)。如下:

  • 可觸及的:從根節(jié)點開始,可以到達這個對象。

  • 可復活的:對象的所有引用都被釋放,但是對象有可能在 finalize()中復活。

  • 不可觸及的:對象的 finalize()被調(diào)用,并且沒有復活,那么就會進入不可觸及狀態(tài)。不可觸及的對象不可能被復活,因為finalize()只會被調(diào)用一次。

以上 3 種狀態(tài)中,是由于 inalize()方法的存在,進行的區(qū)分。只有在對象不可觸及時才可以被回收。

具體過程

判定一個對象 objA 是否可回收,至少要經(jīng)歷兩次標記過程:

  1. 如果對象 objA 到 GC Roots 沒有引用鏈,則進行第一次標記。

  2. 進行篩選,判斷此對象是否有必要執(zhí)行 finalize()方法

  3. 如果對象 objA 沒有重寫 finalize()方法,或者 finalize()方法已經(jīng)被虛擬機調(diào)用過,則虛擬機視為“沒有必要執(zhí)行”,objA 被判定為不可觸及的。

  4. 如果對象 objA 重寫了 finalize()方法,且還未執(zhí)行過,那么 objA 會被插入到 F-Queue 隊列中,由一個虛擬機自動創(chuàng)建的、低優(yōu)先級的 Finalizer 線程觸發(fā)其 finalize()方法執(zhí)行。

  5. finalize()方法是對象逃脫死亡的最后機會,稍后 GC 會對 F-Queue 隊列中的對象進行第二次標記。如果 objA 在 finalize()方法中與引用鏈上的任何一個對象建立了聯(lián)系,那么在第二次標記時,objA 會被移出“即將回收”集合。之后,對象會再次出現(xiàn)沒有引用存在的情況。在這個情況下,finalize 方法不會被再次調(diào)用,對象會直接變成不可觸及的狀態(tài),也就是說,一個對象的 finalize 方法只會被調(diào)用一次。

舉例

public class CanReliveObj {
 ? ?// 類變量,屬于GC Roots的一部分
 ? ?public static CanReliveObj canReliveObj;
?
 ? ?@Override
 ? ?protected void finalize() throws Throwable {
 ? ? ? ?super.finalize();
 ? ? ? ?System.out.println("調(diào)用當前類重寫的finalize()方法");
 ? ? ? ?canReliveObj = this;
 ?  }
?
 ? ?public static void main(String[] args) throws InterruptedException {
 ? ? ? ?canReliveObj = new CanReliveObj();
 ? ? ? ?canReliveObj = null;
 ? ? ? ?System.gc();
 ? ? ? ?System.out.println("-----------------第一次gc操作------------");
 ? ? ? ?// 因為Finalizer線程的優(yōu)先級比較低,暫停2秒,以等待它
 ? ? ? ?Thread.sleep(2000);
 ? ? ? ?if (canReliveObj == null) {
 ? ? ? ? ? ?System.out.println("obj is dead");
 ? ? ?  } else {
 ? ? ? ? ? ?System.out.println("obj is still alive");
 ? ? ?  }
?
 ? ? ? ?System.out.println("-----------------第二次gc操作------------");
 ? ? ? ?canReliveObj = null;
 ? ? ? ?System.gc();
 ? ? ? ?// 下面代碼和上面代碼是一樣的,但是 canReliveObj卻自救失敗了
 ? ? ? ?Thread.sleep(2000);
 ? ? ? ?if (canReliveObj == null) {
 ? ? ? ? ? ?System.out.println("obj is dead");
 ? ? ?  } else {
 ? ? ? ? ? ?System.out.println("obj is still alive");
 ? ? ?  }
?
 ?  }
}

運行結(jié)果

-----------------第一次gc操作------------
調(diào)用當前類重寫的finalize()方法
obj is still alive
-----------------第二次gc操作------------
obj is dead

在第一次 GC 時,執(zhí)行了 finalize 方法,但 finalize()方法只會被調(diào)用一次,所以第二次該對象被 GC 標記并清除了。文章來源地址http://www.zghlxwxcb.cn/news/detail-606687.html

到了這里,關(guān)于對象存活判斷的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 三、JVM-如何判斷對象已死問題

    三、JVM-如何判斷對象已死問題

    體驗與驗證 2.4.5.1 使用visualvm visualgc插件下載鏈接 : https://visualvm.github.io/pluginscenters.html 選擇對應JDK版本鏈接—Tools—Visual GC 若上述鏈接找不到合適的,大家也可以自己在網(wǎng)上下載對應的版本 2.4.5.2 堆內(nèi)存溢出 代碼 記得設(shè)置參數(shù)比如-Xmx20M -Xms20M 運行結(jié)果 訪問 :http://localh

    2024年02月14日
    瀏覽(23)
  • JVM 垃圾回收詳解之內(nèi)存分配和回收原則+死亡對象判斷方法

    JVM 垃圾回收詳解之內(nèi)存分配和回收原則+死亡對象判斷方法

    當需要排查各種內(nèi)存溢出問題、當垃圾收集成為系統(tǒng)達到更高并發(fā)的瓶頸時,我們就需要對這些“自動化”的技術(shù)實施必要的監(jiān)控和調(diào)節(jié)。 Java 的自動內(nèi)存管理主要是針對對象內(nèi)存的回收和對象內(nèi)存的分配。同時,Java 自動內(nèi)存管理最核心的功能是 堆 內(nèi)存中對象的分配與回收

    2023年04月19日
    瀏覽(40)
  • 對象存活判斷

    對象存活判斷

    對象存活判斷 在堆里存放著幾乎所有的 Java 對象實例,在 GC 執(zhí)行垃圾回收之前,首先需要區(qū)分出內(nèi)存中哪些是存活對象,哪些是已經(jīng)死亡的對象。只有被標記為己經(jīng)死亡的對象,GC 才會在執(zhí)行垃圾回收時,釋放掉其所占用的內(nèi)存空間,因此這個過程我們可以稱為垃圾標記階

    2024年02月15日
    瀏覽(37)
  • 《深入理解Java虛擬機》讀書筆記:判斷對象是否存活

    《深入理解Java虛擬機》讀書筆記:判斷對象是否存活

    本節(jié)內(nèi)容的概要如下; 對象已死嗎? ? 給對象中添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器值就加1;當引用失效時,計數(shù)器值就減1;任何時刻計數(shù)器為0的對象就是不可能再被使用的。 客觀地說,引用計數(shù)算法(Reference Counting)的實現(xiàn)簡單,判定效率也很高,在

    2024年02月14日
    瀏覽(146)
  • Jvm對象回收算法-JVM(九)

    Jvm對象回收算法-JVM(九)

    上篇文章介紹了jvm運行時候?qū)ο筮M入老年代的場景,以及如何避免頻繁fullGC。 Jvm參數(shù)設(shè)置-JVM(八) 老年代分配擔保機制 這個機制的目的是為了提升效率,在minorGC之前,會有三次判斷,之后再次minorGC速度會很快。 老年代剩余空間是否 大于 年輕代里現(xiàn)在所有對象 大于的話則

    2024年02月13日
    瀏覽(22)
  • JVM-JVM中對象的結(jié)構(gòu)

    JVM-JVM中對象的結(jié)構(gòu)

    對象內(nèi)存布局 對象里的三個區(qū): 對象頭(Header):Java對象頭占8byte。如果是數(shù)組則占12byte。因為JVM里數(shù)組size需要使用4byte存儲。 標記字段MarkWord: 用于存儲對象自身的運行時數(shù)據(jù),它是synchronized實現(xiàn)輕量級鎖和偏向鎖的關(guān)鍵。 默認存儲:對象HashCode、GC分代年齡、鎖狀態(tài)等

    2024年02月20日
    瀏覽(26)
  • Jvm創(chuàng)建對象之內(nèi)存分配-JVM(七)

    Jvm創(chuàng)建對象之內(nèi)存分配-JVM(七)

    上篇文章介紹了jvm創(chuàng)建,會校驗是否已加載類,沒有則加載,通過之前學的源碼,classLoader加載完之后,虛擬機開始給類分配內(nèi)存,指針移動分配和free鏈表分配,解決并發(fā)分配情況用cap和TLAB方法。之后設(shè)置對象頭部信息,有mark word線程鎖,分代年齡等,klass pointer。還有指針

    2024年02月13日
    瀏覽(25)
  • JVM系列(六) JVM 對象終結(jié)方法finalize

    我們有幾個特別容易混淆的final、finally、finalize, 他們之間的區(qū)別是什么? final 是java finally 是try-catch-finally finalize 是Object 根類的方法 今天我們著重講下 finalize方法 1.finalize方法的原理及調(diào)用 finalize方法也是Java中所有類中必有的方法,因為它是屬于Object根類的方

    2023年04月08日
    瀏覽(21)
  • JVM-JVM中對象的生命周期

    JVM-JVM中對象的生命周期

    申明:文章內(nèi)容是本人學習極客時間課程所寫,文字和圖片基本來源于課程資料,在某些地方會插入一點自己的理解,未用于商業(yè)用途,侵刪。 原資料地址:課程資料 對象的創(chuàng)建 常量池檢查 :檢查new指令是否能在常量池中定位到這個類的符號引用,檢查類之前是否被加載過

    2024年02月20日
    瀏覽(22)
  • JVM 對象引用類型

    對象引用類型分為強引用、軟引用、弱引用和虛引用。 強引用 ????????聲明對象時虛擬機生成的引用,是指在程序代碼之中普遍存在的引用賦值。強引用環(huán)境下,垃圾回收時需要嚴格判斷當前對象是否被強引用,如果被強引用,則不會被垃圾回收,當內(nèi)存空間不足的時候

    2024年02月09日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包