怎么理解java的垃圾收集器

本篇內(nèi)容主要講解“怎么理解java的垃圾收集器”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么理解java的垃圾收集器”吧!

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的吐魯番網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

初識(shí)引用

對(duì)于剛接觸 Java 的 C++ 程序員而言,理解棧和堆的關(guān)系可能很不習(xí)慣。在 C++ 中,可以使用 new  操作符在堆上創(chuàng)建對(duì)象,或者使用自動(dòng)分配在棧上創(chuàng)建對(duì)象。下面的 C++ 語(yǔ)句是合法的,但是 Java 編譯器卻拒絕這么寫(xiě)代碼,會(huì)出現(xiàn) syntax error  編譯錯(cuò)誤。

Integer foo = Integer(1);

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

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

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

怎么理解java的垃圾收集器

首先先來(lái)看一下 foo() 方法,這一行代碼分配了一個(gè)新的 Integer 對(duì)象,JVM 嘗試在堆空間中開(kāi)辟一塊內(nèi)存空間。如果允許分配的話,就會(huì)調(diào)用  Integer 的構(gòu)造方法把 String 字符串轉(zhuǎn)換為 Integer 對(duì)象。JVM 將指向該對(duì)象的指針存儲(chǔ)在變量 baz 中。

上面這種情況是我們樂(lè)意看到的情況,畢竟我們不想在編寫(xiě)代碼的時(shí)候遇到阻礙,但是這種情況是不可能出現(xiàn)的,當(dāng)堆空間無(wú)法為 bar 和 baz  開(kāi)辟內(nèi)存空間時(shí),就會(huì)出現(xiàn) OutOfMemoryError,然后就會(huì)調(diào)用垃圾收集器(garbage collector)  來(lái)嘗試騰出內(nèi)存空間。這中間涉及到一個(gè)問(wèn)題,垃圾收集器會(huì)回收哪些對(duì)象?

垃圾收集器

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

當(dāng)程序嘗試創(chuàng)建新對(duì)象并且堆中沒(méi)有足夠的空間時(shí),垃圾收集器就開(kāi)始工作。當(dāng)收集器訪問(wèn)堆時(shí),請(qǐng)求線程被掛起,試圖查找程序不再主動(dòng)使用的對(duì)象,并回收它們的空間。如果垃圾收集器無(wú)法釋放足夠的內(nèi)存空間,并且JVM  無(wú)法擴(kuò)展堆,則會(huì)出現(xiàn) OutOfMemoryError,你的應(yīng)用程序通常在這之后崩潰。還有一種情況是 StackOverflowError  ,它出現(xiàn)的原因是因?yàn)榫€程請(qǐng)求的棧深度要大于虛擬機(jī)所允許的深度時(shí)出現(xiàn)的錯(cuò)誤。

標(biāo)記 - 清除算法

Java 能永久不衰的一個(gè)原因就是因?yàn)槔占?。許多人認(rèn)為 JVM  會(huì)為每個(gè)對(duì)象保留一個(gè)引用計(jì)數(shù),當(dāng)每次引用對(duì)象的時(shí)候,引用計(jì)數(shù)器的值就 + 1,當(dāng)引用失效的時(shí)候,引用計(jì)數(shù)器的值就 - 1。而垃圾收集器只會(huì)回收引用計(jì)數(shù)器的值為  0 的情況。這其實(shí)是 引用計(jì)數(shù)法(Reference Counting) 的收集方式。但是這種方式無(wú)法解決對(duì)象之間相互引用的問(wèn)題,如下

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;     } }

然而實(shí)際上,JVM 使用一種叫做  標(biāo)記-清除(Mark-Sweep)的算法,標(biāo)記清除垃圾回收背后的想法很簡(jiǎn)單:程序無(wú)法到達(dá)的每個(gè)對(duì)象都是垃圾,可以進(jìn)行回收。

標(biāo)記-清除收集具有如下幾個(gè)階段

  • 階段一:標(biāo)記

垃圾收集器會(huì)從 根(root)  引用開(kāi)始,標(biāo)記它到達(dá)的所有對(duì)象。如果用老師給學(xué)生判斷卷子來(lái)比喻,這就相當(dāng)于是給試卷上的全部答案判斷正確還是錯(cuò)誤的過(guò)程。

怎么理解java的垃圾收集器

  • 階段二:清理

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

怎么理解java的垃圾收集器

  • 階段三:整理(可選)

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

怎么理解java的垃圾收集器

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

因此,返回到 foo() 方法中,僅在執(zhí)行方法時(shí),參數(shù) bar 和局部變量 baz  才是強(qiáng)引用。一旦方法執(zhí)行完成,它們都超過(guò)了作用域的時(shí)候,它們引用的對(duì)象都會(huì)進(jìn)行垃圾回收。

下面來(lái)考慮一個(gè)例子

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

變量 foo 是一個(gè)強(qiáng)引用,它指向一個(gè) LinkedList 對(duì)象。LinkedList(JDK.18)  是一個(gè)鏈表的數(shù)據(jù)結(jié)構(gòu),每一個(gè)元素都會(huì)指向前驅(qū)元素,每個(gè)元素都有其后繼元素。

怎么理解java的垃圾收集器

當(dāng)我們調(diào)用add() 方法時(shí),我們會(huì)增加一個(gè)新的鏈表元素,并且該鏈表元素指向值為 111 的 Integer 實(shí)例。這是一連串的強(qiáng)引用,也就是說(shuō),這個(gè)  Integer 的實(shí)例不符合垃圾收集條件。一旦 foo 對(duì)象超出了程序運(yùn)行的作用域,LinkedList  和其中的引用內(nèi)容都可以進(jìn)行收集,收集的

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

前提是沒(méi)有強(qiáng)引用關(guān)系。

Finalizers

C++  允許對(duì)象定義析構(gòu)函數(shù)方法:當(dāng)對(duì)象超出作用范圍或被明確刪除時(shí),會(huì)調(diào)用析構(gòu)函數(shù)來(lái)清理使用的資源。對(duì)于大多數(shù)對(duì)象來(lái)說(shuō),析構(gòu)函數(shù)能夠釋放使用 new 或者 malloc  函數(shù)分配的內(nèi)存。在Java中,垃圾收集器會(huì)為你自動(dòng)清除對(duì)象,分配內(nèi)存,因此不需要顯式析構(gòu)函數(shù)即可執(zhí)行此操作。這也是 Java 和 C++ 的一大區(qū)別。

然而,內(nèi)存并不是唯一需要被釋放的資源。考慮  FileOutputStream:當(dāng)你創(chuàng)建此對(duì)象的實(shí)例時(shí),它從操作系統(tǒng)分配文件句柄。如果你讓流的引用在關(guān)閉前超過(guò)了其作用范圍,該文件句柄會(huì)怎么樣?實(shí)際上,每個(gè)流都會(huì)有一個(gè)  finalizer 方法,這個(gè)方法是垃圾回收器在回收之前由 JVM 調(diào)用的方法。對(duì)于 FileOutputStream 來(lái)說(shuō),finalizer  方法會(huì)關(guān)閉流,釋放文件句柄給操作系統(tǒng),然后清除緩沖區(qū),確保數(shù)據(jù)能夠?qū)懭氪疟P(pán)。

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

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

雖然 finalizers 的 finalize()  方法是一種好的清除方式,但是這種方法產(chǎn)生的負(fù)面影響非常大,你不應(yīng)該依靠這個(gè)方法來(lái)做任何垃圾回收工作。因?yàn)?finalize  方法的運(yùn)行開(kāi)銷(xiāo)比較大,不確定性強(qiáng),無(wú)法保證各個(gè)對(duì)象的調(diào)用順序。finalize 能做的任何事情,可以使用 try-finally  或者其他方式來(lái)做,甚至做的更好。

對(duì)象的生命周期

綜上所述,可以通過(guò)下面的流程來(lái)對(duì)對(duì)象的生命周期做一個(gè)總結(jié)

怎么理解java的垃圾收集器

對(duì)象被創(chuàng)建并初始化,對(duì)象在運(yùn)行時(shí)被使用,然后離開(kāi)對(duì)象的作用域,對(duì)象會(huì)變成不可達(dá)并會(huì)被垃圾收集器回收。圖中用紅色標(biāo)明的區(qū)域表示對(duì)象處于強(qiáng)可達(dá)階段。

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

怎么理解java的垃圾收集器

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

  • 軟可達(dá):軟可達(dá)就是?我們只能通過(guò)軟引用?才能訪問(wèn)的狀態(tài),軟可達(dá)的對(duì)象是由 SoftReference  引用的對(duì)象,并且沒(méi)有強(qiáng)引用的對(duì)象。軟引用是用來(lái)描述一些還有用但是非必須的對(duì)象。垃圾收集器會(huì)盡可能長(zhǎng)時(shí)間的保留軟引用的對(duì)象,但是會(huì)在發(fā)生  OutOfMemoryError 之前,回收軟引用的對(duì)象。如果回收完軟引用的對(duì)象,內(nèi)存還是不夠分配的話,就會(huì)直接拋出 OutOfMemoryError。

  • 弱可達(dá):弱可達(dá)的對(duì)象是 WeakReference 引用的對(duì)象。垃圾收集器可以隨時(shí)收集弱引用的對(duì)象,不會(huì)嘗試保留軟引用的對(duì)象。

  • 幻象可達(dá):幻象可達(dá)是由 PhantomReference 引用的對(duì)象,幻象可達(dá)就是沒(méi)有強(qiáng)、軟、弱引用進(jìn)行關(guān)聯(lián),并且已經(jīng)被 finalize  過(guò)了,只有幻象引用指向這個(gè)對(duì)象的時(shí)候。

除此之外,還有強(qiáng)可達(dá)和不可達(dá)的兩種可達(dá)性判斷條件

  • 強(qiáng)可達(dá):就是一個(gè)對(duì)象剛被創(chuàng)建、初始化、使用中的對(duì)象都是處于強(qiáng)可達(dá)的狀態(tài)

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

下面是一個(gè)不同可達(dá)性狀態(tài)的轉(zhuǎn)換圖

怎么理解java的垃圾收集器

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

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

ReferenceQueue

引用隊(duì)列又稱為 ReferenceQueue,它位于 java.lang.ref  包下。我們?cè)?建各種引用(軟引用,弱引用,幻象引用)并關(guān)聯(lián)到響應(yīng)對(duì)象?時(shí),可以選擇是否需要關(guān)聯(lián)引用隊(duì)列。JVM  會(huì)在特定的時(shí)機(jī)將引用入隊(duì)到隊(duì)列中,程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入引用,來(lái)了解被引用的對(duì)象是否被GC回收。

Reference

java.lang.ref.Reference 為軟(soft)引用、弱(weak)引用、虛(phantom)引用的父類(lèi)。因?yàn)? Reference 對(duì)象和垃圾回收密切配合實(shí)現(xiàn),該類(lèi)可能不能被直接子類(lèi)化。

到此,相信大家對(duì)“怎么理解java的垃圾收集器”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

當(dāng)前文章:怎么理解java的垃圾收集器
網(wǎng)頁(yè)路徑:http://muchs.cn/article10/ijdjdo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、響應(yīng)式網(wǎng)站、自適應(yīng)網(wǎng)站、網(wǎng)站改版、網(wǎng)站營(yíng)銷(xiāo)、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)