2011-10-14 14 views
3

我的Java教師(高中課程)談論循環和她說,如果你有一個for循環,如:Java垃圾收集 - 它有什麼作用?

for (int i = 0; i < max; i++) { 
    //something 
} 

不能使用變量i外循環,因爲垃圾收集功能刪除它,因爲它的感官,它是「不必要的」(我知道的範圍,並認爲這是因爲BS同樣的事情發生在所有的語言和C++甚至沒有垃圾收集)。現在的問題是......垃圾收集實際上做了什麼? (我看着它,它有什麼做,我不知道還堆所以有人給我講解一下)

感謝

+13

嘆氣,高中教師。 –

+2

建立在別人說的基礎上,垃圾收集在堆上運行,而不是堆棧。堆棧如果存在「範圍」數據,即方法調用中的局部變量。這使得遞歸成爲可能,並且不需要垃圾回收。堆是你的對象和他們的領域中的數據將存活的地方。 –

回答

7

(我知道的範圍,並認爲這是因爲BS同樣的事情發生在所有的語言和C++甚至沒有垃圾收集)。

正確。 變量i由於範圍不能在循環外部使用 - 它具有沒有任何與GC(潛在對象可達性之外)無關。

現在的問題是......垃圾收集實際上做了什麼? (我看着它,它有什麼做,我不知道還堆所以有人給我講解一下),這不再是strongly-reachable

垃圾收集器負責回收對象。垃圾收集器有什麼也沒有 [直接]做變量,雖然變量可以保持對象強烈可及。 (另外,原始值,如int不是對象因此永遠不會被GC ;-)

我會建議通過Chapter 9 of Inside the Java Virtual Machine: Garbage CollectionThe Truth About Garbage Collection讀書,因爲我相信他們會提供足夠的答案/洞察力和理由處理。 (垃圾收集wikipedia entry也是一個良好的開端,並很好地總結了GC一般。)

從「真相」:

對象進入可達狀態時,沒有更多的強引用它存在[是而不是強烈可達]。 當一個對象無法訪問時,它是集合的候選對象。請注意措辭:僅僅因爲對象是收集的候選人並不意味着它會立即收集。 JVM可以自由延遲收集,直到對象立即需要消耗內存爲止。

快樂編碼。

4

老師的例子不是很好,因爲i可能被存儲在堆棧上,因爲它是一個原始的。一個更好的例子是:

public String helloWorld() { 
    StringBuilder builder = new StringBuilder(); 
    builder.append("Hello"); 
    builder.append(" "); 
    builder.append("World!"); 
    return builder.toString(); 
} 

在函數的第一行我們分配一個新對象(new StringBuilder())。這allocates some memory in the heap,後來需要釋放。在C++中,你會做delete builder末來處理(或者它分配在堆棧上 - 但你不能這樣做,在Java中,所以我認爲這是一個合理的例子)。

垃圾收集是的另一種方法,其中沒有反應到builder在函數的末尾。相反,週期性地,一個稱爲垃圾收集器的進程運行,並檢查哪些對象正在使用或未使用,並排除任何未使用的對象。在我給出的示例中,垃圾收集器將運行,注意無法再訪問builder,並將其刪除。

Java的默認的垃圾收集做了所謂的「標記和清除」,它基本上是通過一切可以訪問的變量的散步,標誌着他們(一些標誌設置)。然後刪除任何未標記的內容。

我想在一個更低的水平,它實際上做的是移動一切的訪問到一個新的存儲位置,並在舊存儲位置刪除任何東西(因爲任何事情仍然有不可訪問)。

一個更簡單的垃圾收集方法被稱爲「引用計數」,如該方的動態分配的任何事物都有一個引用計數 - 告訴程序許多變量是如何指向該內存位置。如果引用計數達到0,則沒有人使用該內存,並且可以立即釋放該內存。上次我檢查時,標準的Python解釋器(CPython)使用它。

與引用計數的問題是,你可以得到循環:

class Node { 
    Node next; 
} 

public void breakReferenceCountingAlgorithm() { 
    Node a = new Node(); 
    Node b = new Node(); 
    a.next = b; 
    b.next = a; 
} 

在這個功能,A和B的結束都引用一次(互相),但他們無法訪問。無論如何,Java會捕獲這些垃圾並收集垃圾。 Python不會。


在另一方面,之所以不能使用i之外的循環,你給的範圍界定,而不是垃圾收集。在函數內部,i的內存可能仍然可用,編譯器不會讓你訪問它。主要是讓你可以這樣做:

for(int i = 0; i < 100; i++) { 
    System.out.println("stuff"); 
} 

// This i is a different variable 
for(int i = 0; i < 100; i++) { 
    System.out.println("more stuff"); 
}