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

詳解JVM如何判斷一個對象是否可以被回收

 更新時間:2023年11月23日 08:17:20   作者:Shawn_Shawn  
在c++中,當(dāng)我們使用完某個對象的時候,需要顯示的將對象回收,在java中,jvm會幫助我們進行垃圾回收,無需程序員自己寫代碼進行回收,下面我們就來看看JVM是如何判斷一個對象是否可以被回收的吧

在c++中,當(dāng)我們使用完某個對象的時候,需要顯示的將對象回收,如果忘記回收,則會導(dǎo)致無用對象一直在內(nèi)存里,導(dǎo)致內(nèi)存泄露。在java中,jvm會幫助我們進行垃圾回收,無需程序員自己寫代碼進行回收。

首先jvm需要解決的問題是:如何判斷一個對象是否是垃圾,是否可以被回收呢?一般都是通過引用計數(shù)法,可達性算法。

引用計數(shù)法

對每個對象的引用進行計數(shù),每當(dāng)有一個地方引用它時計數(shù)器+1、引用失效(改為引用其他對象,賦值為null,或者生命周期結(jié)束)則-1,引用的計數(shù)放到對象頭中,大于0的對象被認為是存活對象,一旦某個對象的引用計數(shù)器為 0,則說明該對象已經(jīng)死亡,便可以被回收了。

public void f(){
    Object a = new Object(); // 對象a引用計數(shù)為1
    g(a);
    // 退出g(a),對象b的生命周期結(jié)束,對象a引用計數(shù)為1
}// 退出f(), 對象a的生命周期結(jié)束,引用計數(shù)為0

public void g(Object a){
    Object b = a; // 對象a引用計數(shù)為2
	Object c = a; // 對象a引用計數(shù)為3
    Object d = a; // 對象a引用計數(shù)為4
	d = new Object(); // 對象a引用計數(shù)為3
	c = null; // 對象a引用計數(shù)為2
}

引用計數(shù)法實現(xiàn)起來比較容易,但是存在一個嚴重的問題,那就是無法檢測循環(huán)依賴。如下所示:

public class A{
    public B b;
	public A(){
    
    }
}

public class A{
    public A a;
    public B(){
    
    }
}

A a = new A(); // a的計數(shù)為1
B b = new B(); // b的計數(shù)為1
a.b = b; // b的計數(shù)為2
b.a = a; // a的計數(shù)為2
a = null; // a的計數(shù)為1
b = null; // b的計數(shù)為1

最終a,b的計數(shù)都為1,無法被識別為垃圾,所以無法被回收。

Python使用的就是引用計數(shù)算法,Python的垃圾回收機制,很大一部分是為了處理可能產(chǎn)生的循環(huán)引用,是對引用計數(shù)的補充。

雖然循環(huán)引用的問題可通過Recycler算法解決,但是在多線程環(huán)境下,引用計數(shù)變更也要進行昂貴的同步操作,性能較低,早期的編程語言會采用此算法。

可達性算法

介紹

Java最終并沒有采用引用計數(shù)算法,JVM的主流垃圾回收器采取的是可達性分析算法。

我們把對象之間的引用關(guān)系用數(shù)據(jù)結(jié)構(gòu)中的有向圖來表示。圖中的頂點表示對象。如果對象A中的變量引用了對象B,那么,我們便在對象A對應(yīng)的頂點和對象B對應(yīng)的頂點之間畫一條有向邊。

在有向圖中,有一組特殊的頂點,叫做GC Roots。哪些對象可以作為GC Roots呢?

  • 系統(tǒng)加載的類:rt.jar。
  • JNI handles。
  • 線程運行棧上所有引用,包括方法參數(shù),創(chuàng)建的局部變量等。
  • 已啟動未停止的java線程。
  • 已加載類的靜態(tài)變量。
  • 用于同步的監(jiān)控,調(diào)用了對象的wait()/notify()/notifyAll()。

JVM以GC Roots為起點,遍歷(深度優(yōu)先遍歷或廣度優(yōu)先遍歷)整個圖,可以遍歷到的對象為可達對象,也叫做存活對象,遍歷不到的對象為不可達對象,也叫做死亡對象。死亡對象會被虛擬機當(dāng)做垃圾回收。

JVM實際上采用的是三色算法來遍歷整個圖的,遍歷走過的路徑被稱為reference chain。

  • Black: 對象可達,且對象的所有引用都已經(jīng)掃描了(“掃描”在可以理解成遍歷過了或加入了待遍歷的隊列)
  • Gray: 對象可達,但對象的引用還沒有掃描過(因此 Gray 對象可理解成在搜索隊列里的元素)
  • White: 不可達對象或還沒有掃描過的對象

引用級別

遍歷到的對象一定會存活嗎?事實上,JVM會根據(jù)對象A對對象B的引用強不強烈作出相應(yīng)的回收措施。

基于此JVM根據(jù)引用關(guān)系的強烈,將引用關(guān)系分為四個等級:強引用,軟引用,弱引用,虛幻引用。

強引用

類似Object obj = new Object() 這類的引用都屬于強引用,只要強引用還存在,垃圾回收器永遠不會回收掉被引用的對象,只有在和GC Roots斷絕關(guān)系時,才會被回收。

如果要對強引用進行垃圾回收,需要設(shè)置強引用對象為 null,或者讓其超出對象的生命周期范圍,則認為改對象不存在引用。類似obj = null;

參考代碼:

public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

軟引用

用于描述一些還有用但并非必需的對象。對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出之前,將會把這些對象列進回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常??梢允褂?code>SoftReference 類來實現(xiàn)軟引用。

Object obj = new Object();
SoftReference<Object> softRef = new SoftReference(obj);

弱引用

也是用于描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時,無論當(dāng)前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象??梢允褂?code>WeakReference 類來實現(xiàn)弱引用。

Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference<>(obj);
obj = null;
System.gc();
TimeUnit.SECONDS.sleep(200);
System.out.println(weakReference.get());
System.out.println(weakReference.isEnqueued());

虛引用

它是最弱的一種引用關(guān)系。一個對象是否有虛引用的存在,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來取得一個對象實例。為一個對象設(shè)置一個虛引用關(guān)聯(lián)的唯一目的是能在這個對象被垃圾回收時收到一個系統(tǒng)通知。可以通過PhantomReference 來實現(xiàn)虛引用。

Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(obj, refQueue);
System.out.println(phantomReference.get());
System.out.println(phantomReference.isEnqueued());

基于虛引用,有一個更加優(yōu)雅的實現(xiàn)方式,那就是Java 9以后新加入的Cleaner,用來替代Object類的finalizer方法。

STW

雖然可達性分析的算法本身很簡明,但是在實踐中還是有不少其他問題需要解決的。我們把運行應(yīng)用程序的線程叫做用戶線程,把執(zhí)行垃圾回收的線程叫做垃圾回收線程,如果在執(zhí)行垃圾回收線程的同時還在執(zhí)行用戶線程,那么對象的引用關(guān)系可能會在垃圾回收途中被用戶線程修改,從而造成誤報(將引用設(shè)置為 null)或者漏報(將引用設(shè)置為未被訪問過的對象)

誤報并沒有什么傷害,Java 虛擬機至多損失了部分垃圾回收的機會。漏報則比較麻煩,因為垃圾回收器可能回收事實上仍被引用的對象內(nèi)存,導(dǎo)致程序出錯。

為了解決漏報的問題,保證垃圾回收線程不會被用戶線程打擾,最簡單粗暴的方式就是在垃圾回收的過程中,暫停用戶線程,直到垃圾回收結(jié)束,再恢復(fù)用戶線程,這就是STW(STOP THE WORLD)。

但是如果STW的時間過程,就會嚴重影響程序的性能,因此優(yōu)化垃圾回收過程,盡量減少STW的時間,是垃圾回收器努力優(yōu)化的方向,

安全點

上述除了STW的響應(yīng)時間的問題,還有另外一個問題,就是如何從一個正確的狀態(tài)停止,再從這個狀態(tài)正確恢復(fù)。Java虛擬機中的STW是通過安全點(safepoint)機制來實現(xiàn)的。當(dāng)Java虛擬機收到STW請求,它便會等待所有的線程都到達安全點,才允許請求Stop-the-world的線程進行獨占的工作。

當(dāng)然,安全點的初始目的并不是讓用戶線程立刻停下,而是找到一個穩(wěn)定的執(zhí)行狀態(tài)。在這個執(zhí)行狀態(tài)下,JVM的堆棧不會發(fā)生變化。這么一來,垃圾回收器便能夠“安全”地執(zhí)行可達性分析,才能找到完整GC Roots。

是不是所有的用戶線程在垃圾回收的時候都要停止呢?實際上,JVM也做了優(yōu)化,如果某個線程處于安全區(qū)(不會改變對象引用關(guān)系的一段連續(xù)的代碼區(qū)間),那么這個線程不需要停止,可以和垃圾回收線程并行執(zhí)行。一旦離開安全區(qū),JVM會檢查是否處于STW階段,如果是,則需要阻塞該線程,等垃圾回收完再恢復(fù)。

以上就是詳解JVM如何判斷一個對象是否可以被回收的詳細內(nèi)容,更多關(guān)于JVM對象回收的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java實現(xiàn)布隆過濾器的示例詳解

    Java實現(xiàn)布隆過濾器的示例詳解

    布隆過濾器(Bloom?Filter)是1970年由布隆提出來的,實際上是由一個很長的二進制數(shù)組+一系列hash算法映射函數(shù),用于判斷一個元素是否存在于集合中。本文主要介紹了Java實現(xiàn)布隆過濾器的示例代碼,希望對大家有所幫助
    2023-03-03
  • 一篇文章帶你入門Java數(shù)據(jù)結(jié)構(gòu)

    一篇文章帶你入門Java數(shù)據(jù)結(jié)構(gòu)

    這篇文章主要介紹了Java常見數(shù)據(jù)結(jié)構(gòu)面試題,帶有答案及解釋,希望對廣大的程序愛好者有所幫助,同時祝大家有一個好成績,需要的朋友可以參考下,希望可以幫助到你
    2021-08-08
  • SpringBoot中TypeExcludeFilter的作用及使用方式

    SpringBoot中TypeExcludeFilter的作用及使用方式

    在SpringBoot應(yīng)用程序中,TypeExcludeFilter通過過濾特定類型的組件,使它們不被自動掃描和注冊為bean,這在排除不必要的組件或特定實現(xiàn)類時非常有用,通過創(chuàng)建自定義過濾器并注冊到spring.factories文件中,我們可以在應(yīng)用啟動時生效
    2025-01-01
  • HDFS的Java API的訪問方式實例代碼

    HDFS的Java API的訪問方式實例代碼

    這篇文章主要介紹了HDFS的Java API的訪問方式實例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • Java中獲取webapp路徑問題詳解

    Java中獲取webapp路徑問題詳解

    這篇文章主要介紹了Java中獲取webapp路徑問題詳解,WebApp是指基于Web的?系統(tǒng)和?應(yīng)用,其作用是向廣大的最終用戶發(fā)布一組復(fù)雜的內(nèi)容和功能,本文詳解了關(guān)于獲取路徑時候可能出現(xiàn)的問題,需要的朋友可以參考下
    2023-07-07
  • Data Source與數(shù)據(jù)庫連接池簡介(JDBC簡介)

    Data Source與數(shù)據(jù)庫連接池簡介(JDBC簡介)

    DataSource是作為DriverManager的替代品而推出的,DataSource 對象是獲取連接的首選方法,這篇文章主要介紹了Data Source與數(shù)據(jù)庫連接池簡介(JDBC簡介),需要的朋友可以參考下
    2022-11-11
  • 一學(xué)即會之JDK版本快速切換方法(2024)

    一學(xué)即會之JDK版本快速切換方法(2024)

    這篇文章主要介紹了一學(xué)即會之JDK版本快速切換方法,詳細給大家講解了如何下載、安裝和配置多個JDK版本,并通過設(shè)置環(huán)境變量和編寫批處理腳本來切換JDK版本,需要的朋友可以參考下
    2025-03-03
  • Java如何利用return結(jié)束方法調(diào)用

    Java如何利用return結(jié)束方法調(diào)用

    這篇文章主要介紹了Java如何利用return結(jié)束方法調(diào)用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Java 遍歷list和map的方法

    Java 遍歷list和map的方法

    這篇文章主要介紹了Java 遍歷list和map的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • IDEA的Terminal無法執(zhí)行g(shù)it命令問題

    IDEA的Terminal無法執(zhí)行g(shù)it命令問題

    這篇文章主要介紹了IDEA的Terminal無法執(zhí)行g(shù)it命令問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評論