亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么理解java的垃圾收集器

發布時間:2021-11-03 10:57:24 來源:億速云 閱讀:146 作者:iii 欄目:編程語言

本篇內容主要講解“怎么理解java的垃圾收集器”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么理解java的垃圾收集器”吧!

初識引用

對于剛接觸 Java 的 C++ 程序員而言,理解棧和堆的關系可能很不習慣。在 C++ 中,可以使用 new  操作符在堆上創建對象,或者使用自動分配在棧上創建對象。下面的 C++ 語句是合法的,但是 Java 編譯器卻拒絕這么寫代碼,會出現 syntax error  編譯錯誤。

Integer foo = Integer(1);

Java 和 C 不一樣,Java 中會把對象都放在堆上,需要 new  操作符來創建對象。本地變量存儲在棧中,它們持有一個指向堆中對象的引用(指針)。下面是一個 Java 方法,該方法具有一個 Integer 變量,該變量從  String 解析值

public static void foo(String bar){     Integer baz = new Integer(bar); }

這段代碼我們使用堆棧分配圖可以看一下它們的關系

怎么理解java的垃圾收集器

首先先來看一下 foo() 方法,這一行代碼分配了一個新的 Integer 對象,JVM 嘗試在堆空間中開辟一塊內存空間。如果允許分配的話,就會調用  Integer 的構造方法把 String 字符串轉換為 Integer 對象。JVM 將指向該對象的指針存儲在變量 baz 中。

上面這種情況是我們樂意看到的情況,畢竟我們不想在編寫代碼的時候遇到阻礙,但是這種情況是不可能出現的,當堆空間無法為 bar 和 baz  開辟內存空間時,就會出現 OutOfMemoryError,然后就會調用垃圾收集器(garbage collector)  來嘗試騰出內存空間。這中間涉及到一個問題,垃圾收集器會回收哪些對象?

垃圾收集器

Java 給你提供了一個 new 操作符來為堆中的對象開辟內存空間,但它沒有提供 delete 操作符來釋放對象空間。當 foo() 方法返回時,如果變量  baz 超過最大內存,但它所指向的對象仍然還在堆中。如果沒有垃圾回收器的話,那么程序就會拋出 OutOfMemoryError 錯誤。然而 Java  不會,它會提供垃圾收集器來釋放不再引用的對象。

當程序嘗試創建新對象并且堆中沒有足夠的空間時,垃圾收集器就開始工作。當收集器訪問堆時,請求線程被掛起,試圖查找程序不再主動使用的對象,并回收它們的空間。如果垃圾收集器無法釋放足夠的內存空間,并且JVM  無法擴展堆,則會出現 OutOfMemoryError,你的應用程序通常在這之后崩潰。還有一種情況是 StackOverflowError  ,它出現的原因是因為線程請求的棧深度要大于虛擬機所允許的深度時出現的錯誤。

標記 - 清除算法

Java 能永久不衰的一個原因就是因為垃圾收集器。許多人認為 JVM  會為每個對象保留一個引用計數,當每次引用對象的時候,引用計數器的值就 + 1,當引用失效的時候,引用計數器的值就 - 1。而垃圾收集器只會回收引用計數器的值為  0 的情況。這其實是 引用計數法(Reference Counting) 的收集方式。但是這種方式無法解決對象之間相互引用的問題,如下

class A{   public B b;  } class B{   public A a; } public class Main{     public static void main(String[] args){     A a = new A();     B b = new B();     a.b=b;     b.a=a;     } }

然而實際上,JVM 使用一種叫做  標記-清除(Mark-Sweep)的算法,標記清除垃圾回收背后的想法很簡單:程序無法到達的每個對象都是垃圾,可以進行回收。

標記-清除收集具有如下幾個階段

  • 階段一:標記

垃圾收集器會從 根(root)  引用開始,標記它到達的所有對象。如果用老師給學生判斷卷子來比喻,這就相當于是給試卷上的全部答案判斷正確還是錯誤的過程。

怎么理解java的垃圾收集器

  • 階段二:清理

在第一階段中所有可回收的的內容都能夠被垃圾收集器進行回收。如果一個對象被判定為是可以回收的對象,那么這個對象就被放在一個 finalization  queue(回收隊列)中,并在稍后會由一個虛擬機自動建立的、低優先級的 finalizer 線程去執行它。

怎么理解java的垃圾收集器

  • 階段三:整理(可選)

一些收集器有第三個步驟,整理。在這個步驟中,GC  將對象移動到垃圾收集器回收完對象后所留下的自由空間中。這么做可以防止堆碎片化,防止大對象在堆中由于堆空間的不連續性而無法分配的情況。

怎么理解java的垃圾收集器

所以上面的過程中就涉及到一個根節點(GC Roots) 來判斷是否存在需要回收的對象。這個算法的基本思想就是通過一系列的 GC Roots  作為起始點,從這些節點向下搜索,搜索所走過的路徑稱為 引用鏈(Reference Chain),當一個對象到 GC Roots  之間沒有任何引用鏈相連的話,則證明此對象不可用。引用鏈上的任何一個能夠被訪問的對象都是強引用 對象,垃圾收集器不會回收強引用對象。

因此,返回到 foo() 方法中,僅在執行方法時,參數 bar 和局部變量 baz  才是強引用。一旦方法執行完成,它們都超過了作用域的時候,它們引用的對象都會進行垃圾回收。

下面來考慮一個例子

LinkedList foo = new LinkedList(); foo.add(new Integer(111));

變量 foo 是一個強引用,它指向一個 LinkedList 對象。LinkedList(JDK.18)  是一個鏈表的數據結構,每一個元素都會指向前驅元素,每個元素都有其后繼元素。

怎么理解java的垃圾收集器

當我們調用add() 方法時,我們會增加一個新的鏈表元素,并且該鏈表元素指向值為 111 的 Integer 實例。這是一連串的強引用,也就是說,這個  Integer 的實例不符合垃圾收集條件。一旦 foo 對象超出了程序運行的作用域,LinkedList  和其中的引用內容都可以進行收集,收集的

protected void finalize() throws Throwable {     // 清除對象 }

前提是沒有強引用關系。

Finalizers

C++  允許對象定義析構函數方法:當對象超出作用范圍或被明確刪除時,會調用析構函數來清理使用的資源。對于大多數對象來說,析構函數能夠釋放使用 new 或者 malloc  函數分配的內存。在Java中,垃圾收集器會為你自動清除對象,分配內存,因此不需要顯式析構函數即可執行此操作。這也是 Java 和 C++ 的一大區別。

然而,內存并不是唯一需要被釋放的資源。考慮  FileOutputStream:當你創建此對象的實例時,它從操作系統分配文件句柄。如果你讓流的引用在關閉前超過了其作用范圍,該文件句柄會怎么樣?實際上,每個流都會有一個  finalizer 方法,這個方法是垃圾回收器在回收之前由 JVM 調用的方法。對于 FileOutputStream 來說,finalizer  方法會關閉流,釋放文件句柄給操作系統,然后清除緩沖區,確保數據能夠寫入磁盤。

任何對象都具有 finalizer 方法,你要做的就是聲明 finalize() 方法。如下

protected void finalize() throws Throwable {     // 清除對象 }

雖然 finalizers 的 finalize()  方法是一種好的清除方式,但是這種方法產生的負面影響非常大,你不應該依靠這個方法來做任何垃圾回收工作。因為 finalize  方法的運行開銷比較大,不確定性強,無法保證各個對象的調用順序。finalize 能做的任何事情,可以使用 try-finally  或者其他方式來做,甚至做的更好。

對象的生命周期

綜上所述,可以通過下面的流程來對對象的生命周期做一個總結

怎么理解java的垃圾收集器

對象被創建并初始化,對象在運行時被使用,然后離開對象的作用域,對象會變成不可達并會被垃圾收集器回收。圖中用紅色標明的區域表示對象處于強可達階段。

JDK1.2 介紹了 java.lang.ref 包,對象的生命周期有四個階段:?強可達?(Strongly  Reachable?)、軟可達(Soft Reachable?)、弱可達(Weak Reachable?)、 幻象可達(Phantom  Reachable?)。

怎么理解java的垃圾收集器

如果只討論符合垃圾回收條件的對象,那么只有三種:軟可達、弱可達和幻象可達。

  • 軟可達:軟可達就是?我們只能通過軟引用?才能訪問的狀態,軟可達的對象是由 SoftReference  引用的對象,并且沒有強引用的對象。軟引用是用來描述一些還有用但是非必須的對象。垃圾收集器會盡可能長時間的保留軟引用的對象,但是會在發生  OutOfMemoryError 之前,回收軟引用的對象。如果回收完軟引用的對象,內存還是不夠分配的話,就會直接拋出 OutOfMemoryError。

  • 弱可達:弱可達的對象是 WeakReference 引用的對象。垃圾收集器可以隨時收集弱引用的對象,不會嘗試保留軟引用的對象。

  • 幻象可達:幻象可達是由 PhantomReference 引用的對象,幻象可達就是沒有強、軟、弱引用進行關聯,并且已經被 finalize  過了,只有幻象引用指向這個對象的時候。

除此之外,還有強可達和不可達的兩種可達性判斷條件

  • 強可達:就是一個對象剛被創建、初始化、使用中的對象都是處于強可達的狀態

  • 不可達(unreachable):處于不可達的對象就意味著對象可以被清除了。

下面是一個不同可達性狀態的轉換圖

怎么理解java的垃圾收集器

判斷可達性條件,也是 JVM 垃圾收集器決定如何處理對象的一部分考慮因素。

所有的對象可達性引用都是 java.lang.ref.Reference 的子類,它里面有一個get()  方法,返回引用對象。如果已通過程序或垃圾收集器清除了此引用對象,則此方法返回 null  。也就是說,除了幻象引用外,軟引用和弱引用都是可以得到對象的。而且這些對象可以人為拯救,變為強引用,例如把 this  關鍵字賦值給對象,只要重新和引用鏈上的任意一個對象建立關聯即可。

ReferenceQueue

引用隊列又稱為 ReferenceQueue,它位于 java.lang.ref  包下。我們在?建各種引用(軟引用,弱引用,幻象引用)并關聯到響應對象?時,可以選擇是否需要關聯引用隊列。JVM  會在特定的時機將引用入隊到隊列中,程序可以通過判斷引用隊列中是否已經加入引用,來了解被引用的對象是否被GC回收。

Reference

java.lang.ref.Reference 為軟(soft)引用、弱(weak)引用、虛(phantom)引用的父類。因為  Reference 對象和垃圾回收密切配合實現,該類可能不能被直接子類化。

到此,相信大家對“怎么理解java的垃圾收集器”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

福建省| 武宣县| 保靖县| 喀什市| 竹北市| 香港| 尉氏县| 桃江县| 公主岭市| 合作市| 赤壁市| 呼图壁县| 错那县| 宁德市| 措美县| 九台市| 都江堰市| 靖安县| 岑巩县| 闵行区| 项城市| 甘洛县| 聂拉木县| 赞皇县| 山阴县| 福建省| 汶上县| 海盐县| 徐汇区| 杭州市| 多伦县| 治多县| 崇信县| 寻乌县| 抚松县| 秦皇岛市| 黄浦区| 临夏县| 六安市| 深州市| 漳浦县|