Java GC 判斷對象是否存活(轉(zhuǎn))

幾個疑問:

棧區(qū):也叫java虛擬機棧,是由一個一個的棧幀組成的后進先出的棧式結(jié)構(gòu),棧楨中存放方法運行時產(chǎn)生的局部變量、方法出口等信息。當調(diào)用一個方法時,虛擬機棧中就會創(chuàng)建一個棧幀存放這些數(shù)據(jù),當方法調(diào)用完成時,棧幀消失,如果方法中調(diào)用了其他方法,則繼續(xù)在棧頂創(chuàng)建新的棧楨。

總結(jié):一個線程對應(yīng)一個Java棧,一個方法對應(yīng)一個棧幀。

0、GC怎么判斷堆中一個對象有沒有被引用?

java在方法里new一個對象,該對象的生命周期及作用范圍

生命周期取決于他有沒有被引用,會一直持續(xù)到?jīng)]有變量引用他,作用范圍取決于引用他的變量的作用范圍。

總結(jié):Java大概設(shè)計:

(1)堆中的對象,從創(chuàng)建(在堆中生存時)會攜帶一個變量引用它的標記。(大概會有被變量引用的標記,否則GC怎么知道堆中對象有沒有被引用?)

舉例:比如一個局部變量user,如果是需要手動釋放內(nèi)存的c++語言,在它即將離開作用域時,(假定該對象不會被其它對象使用)就會釋放user指向的內(nèi)存空間。

在java中,怎么釋放user對象呢?它所在方法執(zhí)行時,創(chuàng)建了一個棧幀在Java棧頂入棧,user引用在這個棧幀中。(隨著棧幀的創(chuàng)建,堆中user對象也生成了?,該對象也被賦予一個被user引用的標記),隨著方法的執(zhí)行,user引用會出棧(堆中對象會清除該對象被該變量引用的標記)。(假定該對象沒有被其他對象持有),在下一輪GC運行時,就會發(fā)現(xiàn)該對象沒有被引用,再查看堆中其它對象時,又發(fā)現(xiàn)沒有任何對象持有該user對象,那么該user對象就會被清空釋放內(nèi)存。

(注:持有對象,對象之間的持有,每個對象在內(nèi)存都是獨立占有空間,對象之間持有時,大概是做一個標記就行,標記就是在當前對象中標記一下被持有對象的地址。大概不能說持有對象的引用。和引用沒關(guān)系,被持有對象有沒有被變量引用,和當前持有者沒有關(guān)系。持有者可以使用該對象,變量引用也可以使用該對象)

(2)比如:有兩個局部變量

User user1 = new User(); // 離開當前作用域之后,GC應(yīng)該會清除user1指向的對象

User user2 = session.secuObj.getUser(); // 離開作用域之后,由于user2指向的對象仍然被全局變量?session持有。所以user2指向的對象,不會被清除。

(3)兩個成員變量

User user2 = Session.secuObj.getUser(); // 該user2對象被全局變量?持有,生命周期應(yīng)該和程序一樣長。

User user1 = new User(); // 普通的成員變量, 怎么理解?

1)、user1所屬的類進行實例化時,user1對象在堆中生成,并被標記被user1變量引用了。

2)所在類的各個方法使用user1變量時,方法結(jié)束user1出棧后,由于user1是成員變量,所以指向的對象還是在被user1變量引用,該標記不會被清除。(所在類的其它方法還可能會使用user1)

3)user1是成員字段,會被所屬類的當前對象所持有。

? ? ? ? 3.1、成員字段的生命周期和它所屬對象一樣長?往上遞歸?什么時候結(jié)束?

? ? ? ? 3.2、user1堆中對象什么時候失去被user1變量引用的的標記?


1、作用域

在計算機程序中,聲明在不同地方的變量具有不同的作用域,例如局部變量、全局變量等。在Java語言中,作用域是由花括號的位置決定的,它決定了其定義的變量名的可見性與生命周期。

在Java語言中,變量的類型主要有3種:成員變量、靜態(tài)變量和局部變量。

類的成員變量的作用范圍與類的實例化對象的作用范圍相同,當類被實例化時,成員變量就會在內(nèi)存中分配空間并初始化,直到這個被實例化對象的生命周期結(jié)束時,成員變量的生命周期才結(jié)束。(?是這樣嗎)

成員變量與它所在類的實例對象的生命周期一致,那么對象的生命周期?

被static修飾的成員變量稱為靜態(tài)變量或全局變量,與成員變量不同的是,靜態(tài)變量不依賴于特定的實例,而是被所有實例所共享,也就是說,只要一個類被加載,JVM就會給類的靜態(tài)變量分配存儲空間。因此,就可以通過類名和變量名來訪問靜態(tài)變量。

局部變量的作用域與可見性為它所在的花括號內(nèi)。

同一作用域范圍的包裹下成員變量名和局部變量名是可以變量名相同的。在方法中使用變量時,如果不指明使用成員變量還是局部變量,那么默認使用局部變量(就近原則),但是如果局部變量超出了它本身的作用域范圍則會失效,被JVM垃圾回收,(不管會不會被回收)那么則可以重復(fù)命名此變量(同一作用域范圍內(nèi),局部變量不能重名)。

2、對象的生命周期

3、函數(shù)執(zhí)行的堆棧分析


正文:

出自:http://www.lxweimin.com/p/49192175c759

Java對象是否存活的判斷算法——根搜索算法。(這是判斷對象是否存活的算法,不同于GC算法)

這個算法的思路其實很簡單,它把內(nèi)存中的每一個對象都看作一個節(jié)點,并且定義了一些對象作為根節(jié)點“GC Roots”。如果一個對象中有另一個對象的引用,那么就認為第一個對象有一條指向第二個對象的邊,如下圖所示。JVM會起一個線程從所有的GC Roots開始往下遍歷,當遍歷完之后如果發(fā)現(xiàn)有一些對象不可到達,那么就認為這些對象已經(jīng)沒有用了,需要被回收。


四種作為GC Roots的對象==>

1、虛擬機棧中的引用的對象,我們在程序中正常創(chuàng)建一個對象,對象會在堆上開辟一塊空間,同時會將這塊空間的地址作為引用保存到虛擬機棧中,如果對象生命周期結(jié)束了,那么引用就會從虛擬機棧中出棧,因此如果在虛擬機棧中有引用,就說明這個對象還是有用的,這種情況是最常見的。

2、我們在類中定義了全局的靜態(tài)的對象,也就是使用了static關(guān)鍵字,由于虛擬機棧是線程私有的,所以這種對象的引用會保存在共有的方法區(qū)中,顯然將方法區(qū)中的靜態(tài)引用作為GC Roots是必須的。

3、常量引用,就是使用了static final關(guān)鍵字,由于這種引用初始化之后不會修改,所以方法區(qū)常量池里的引用的對象也應(yīng)該作為GC Roots。

4、使用JNI技術(shù)時,有時候單純的Java代碼并不能滿足我們的需求,我們可能需要在Java中調(diào)用C或C++的代碼,因此會使用native方法,JVM內(nèi)存中專門有一塊本地方法棧,用來保存這些對象的引用,所以本地方法棧中引用的對象也會被作為GC Roots。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容