2011-03-18 71 views
2

我知道,在Java的垃圾收集期間,沒有任何更多對它們的引用的對象被標記爲「死」,以便它們可以被垃圾收集器從內存中刪除。未被垃圾收集的Java「死」對象

我的問題是,如果在垃圾收集階段,所有的「死」對象被從內存中刪除或其中一些生存?爲什麼一個「死」的對象在垃圾收集階段生存?

以後編輯

感謝您對所有的答案的。我可以推斷,「死」對象不會被刪除的主要原因是由於垃圾收集器運行方式的時間或空間限制。 但是,假設垃圾收集器可以訪問所有的「死」對象,我想知道是否有一種方法來聲明,引用,使用,取消引用等。一個對象,以某種方式它會跳過刪除階段,甚至儘管它已經「死了」。我在想,也許屬於有靜態方法或內部類的類的對象可能會因爲某些原因而被保留在內存中,即使它們沒有對它們的引用。
這種情況可能嗎?

謝謝

+1

垃圾收集完成後,爲什麼會有_dead_對象存活。究竟。它不會。在JVM執行GC之後會有一個標準,緊接着,不會是一個死對象。 – Nishant 2011-03-18 09:17:39

+0

它不完全關於refcounting - 對象的「島嶼」也被收集。 Nishant是對的 - 收集死者的物品。期間 – kostja 2011-03-18 09:22:52

+0

@Nishant:它比一個有資格獲得GC的對象更容易成爲一個GC。特別適用於代GC:如果任職空間中的對象符合條件,並且JVM在eden空間中執行GC,那麼符合條件的對象將在後續執行。 – 2011-03-18 12:32:54

回答

0

垃圾收集器的行爲沒有完全指定。如果特定的實現選擇不收集某些對象,則允許這樣做。這可以避免在垃圾收集器中花費大量時間,這可能會對應用程序的運行產生不利影響。

3

由於System.gc() javadoc

當從方法 呼叫控制返回時,Java虛擬機已經 做出從所有丟棄的對象中回收了空間 盡力而爲。

從中可以推斷出對垃圾收集器的調用不能保證所有未使用的對象都將被回收。由於垃圾收集可能在實現之間完全不同,因此不能給出明確的答案。甚至有java實現without any garbage collection

+0

我敢打賭,OP已經測試了一個impl。 System.gc()完成一個完整的集合。雖然可以禁用顯式GC,但這個答案並不涉及這個問題。 – bestsss 2011-03-18 09:46:04

+0

我認爲你錯了,這個op正好問他的垃圾收集器是否做了一個完整的收集,這是無法回答沒有上下文。 – 2011-03-18 09:55:05

2
  • 「年輕一代」存在死物,「舊」一代存在死物。如果GC在「次GC」中執行,則只收集年輕一代的死對象。
  • 此外,還可以使用finalize()方法通過從finalize()拋出異常收集你的對象停止VM(至少,這就是我所理解Object.finalize()的javadoc:finalize方法拋出的任何異常導致此對象的終結是停止,但在其他方面被忽略)。
+0

*此外,您可以使用finalize()方法停止VM通過從finalize()拋出異常來收集對象*拋出異常對對象的集合沒有任何影響。可以通過finalize()重新生成一個對象,但這不是重點,'finalize()'拋出異常對虛擬機不起任何作用。 – bestsss 2011-03-18 09:28:02

+0

@bestsss所以你同意拋出異常會阻止GC回收對象的內存,或者你沒有? :) – 2011-03-18 09:33:06

+1

不,它將沒有影響。如果您復活它,對象將不會被收回。拋出異常不會做任何事情。你可以看看'java.lang.ref.Finalizer',它是一個類似於WeakReference的引用;停止最終確定與回收內存無關。它只是不執行finalize方法(除了我提到過的幾次復活之外,它與垃圾回收沒有關係) – bestsss 2011-03-18 09:38:00

3

不可收集的對象沒有被收集的一個潛在的解釋是時間。從Java 1.5開始,JVM花費在垃圾收集上的時間可以通過以下選項來限制:

  • -XX:MaxGCPauseMillis
  • -XX:GCTimeRatio=<nnn>

兩個選項進行了詳細的解釋here

+2

這些標誌不具有停止垃圾回收的效果。相反,它們通常會導致JVM在完成當前GC循環後拋出OOME *。 – 2011-03-18 09:37:22

+0

@Stephen - 謝謝你! – mmccomb 2011-03-18 09:52:50

+0

這只是關於parralel GC而已 – gstackoverflow 2016-12-27 10:36:05

7

我的問題是,如果在垃圾收集階段,所有的 「死」 的對象被刪除從記憶還是其中一些生存?爲什麼一個「死」的對象在垃圾收集階段生存?

所有當前的HotSpot GC都是世代收藏家。從維基百科報價:

「經驗已經觀察到,在許多程序,最近創建的對象也是那些最有可能迅速成爲不可達(被稱爲嬰兒死亡率或代假設)分代GC (也被稱爲短暫GC)將對象劃分爲幾代,並且在大多數週期中,將僅將一代子集的對象放入初始白色(有瑕疵)集合中。此外,運行時系統通過觀察創建和覆蓋引用當垃圾收集器運行時,它可以使用這些知識來證明初始白集中的一些對象在不必遍歷整個引用樹的情況下是不可訪問的。 sis成立,這會導致更快的收集週期,同時還能收回大部分無法訪問的對象。「

這對於您的問題意味着什麼,大多數GC循環只收集年輕一代的垃圾對象。最老一代的垃圾對象可以經歷多個GC循環...直到最終收集到老一代。 (而在新的G1 GC,顯然是老一代收集了一下,在一個時間...它甚至可以進一步延遲迴收)

其他原因(名義上)不可達的對象生存包括:

  • 無法訪問的對象(未執行的)終結器在GC完成後由垃圾收集器附加到終止隊列以供處理。

  • 軟,弱或幻影引用的對象實際上仍然可以訪問,GC完成後由其各自的引用隊列管理器處理。

  • 憑藉JNI全局引用等可訪問的對象。 (謝謝@ bestss)

  • 存在各種隱藏的引用關聯實例,它們的類和它們的類加載器。

  • 有一個從內部實例到其外部實例的隱藏引用。

  • 對於表示其字符串文字的intern'd String對象,存在從類的隱藏引用。

然而,這些都是可訪問性的定義的所有後果:

「A可及對象是可以從任何活動線程的任何潛在的持續的計算進行訪問的對象。」 - JLS 12.6.1

這也是值得注意的是,對於GC規則到處都有保守的元素。他們說,一個可到達的對象不會被刪除,但他們並不會說(嚴格地說)不可達的對象將被刪除。這允許在不能訪問對象但運行時系統無法確定的情況下。


你的後續問題:

然而,假定垃圾收集器能達到所有的「死」的對象,我在想,如果有申報,參考,使用非關聯化的方式等等。一個對象,即使它「死」,它也會跳過刪除階段。

「死」不是一個明確定義的術語。如果垃圾收集器可以訪問這些對象,則它們通過定義可以被訪問。當它們仍然可以到達時,它們不會被刪除。

如果它們都是死的並且可到達(無論「死」是什麼意思!),那麼它們可達的事實意味着它們不會被刪除。

你提出的建議沒有意義。

我在想可能屬於類的靜態方法或內部類或類似的東西可能會因爲某些原因保存在內存中,即使它們沒有引用它們。這種情況可能嗎?

靜態方法沒有引用......除非它們碰巧在調用堆棧上。然後局部變量可能像其他方法調用一樣包含引用。正常可達性規則適用。

只要類本身存在,靜態字段就是GC根。正常可達性規則適用。

從GC的角度來看,內部類的實例與其他類的實例沒有什麼不同。可以在內部類實例中引用外部類實例,但會導致正常的可達性。

總之,可達性有一些意想不到的「原因」,但它們都是可達性定義的邏輯結果。

+0

不知道爲什麼人們對GC過程提出如此多的問題,甚至沒有閱讀任何基礎知識,但我希望你的回答能夠提供一些光線:)。除了由於Weak/Phantom/Final/etc引用引起的入隊外,由於全局JNI引用引起的「無法訪問的對象」,這些引用不是衆所周知的,但被GC視爲根引用。 – bestsss 2011-03-18 09:59:34

+0

@bestsss你的第二句沒有動詞。你能更具體地瞭解你的評論/回答嗎?謝謝。 – 2011-03-18 13:10:06

+0

@Victor,動詞應該是*可以直到達到* – bestsss 2011-03-18 13:11:42

0

想象一下,您有一個包含數百萬個小物件的集合,其中大部分未在其他地方引用。如果只有對該集合的引用已被清除,您是否希望GC花費很長時間來清理這些數百萬個小對象,或者是否希望在多次調用過程中這樣做?在大多數情況下,後者對於應用程序會更好。

+0

實際上我更喜歡他們是通過一些免費核心在後臺收集的:)) – bestsss 2011-03-18 10:06:05

+0

居然(2)...典型的複製GC非常擅長處理這一點。不是垃圾的對象被複制到其他地方,而其餘的只是被清零。 – 2011-03-18 10:11:01

+0

通常情況是的,但考慮到沒有免費的核心(可能沒有),並且可能存在可憐的壓縮(可能),可能一個週期不會完成(或者至少曾經是這樣,我只是很少回答Java Qs,因爲與其他領域相比,我不是最新的)。 – 2011-03-18 10:20:36