2011-05-24 197 views
1

Java垃圾收集器的性能我有一個程序,其中有問題的循環看起來像這樣分配/釋放內存內循環

int numOfWords = 1000; 
int avgSizeOfWord = 20; 
while(all documents are not read) { 
    char[][] wordsInDoc = new char[numOfWords][avgSizeOfWord]; 
    for(int i=0; i<numWordsInDoc; i++) { 
     wordsInDoc[i] = getNextWord(); 
    } 
    processWords(wordsInDoc); 
} 

我想知道,當這個循環被執行了什麼幕後發生。垃圾收集器何時收集爲每個文檔分配的內存?他們是一個更好的方式(內存使用)做同樣的事情嗎?

任何洞察力是讚賞。

+0

看看:http://stackoverflow.com/questions/4138200/garbage-collection-on-a-local-variable – 2011-05-24 18:27:12

+0

Java有一個非確定性的垃圾收集器,這意味着沒有辦法預測它的行爲只是通過查看一段代碼。 – 2011-05-24 18:27:15

+0

你的問題可以說比JIT更適合JIT,也就是說如果這個代碼被JIT編譯,那麼你的代碼的版本是什麼樣的?例如processWords有什麼作用?它會被內聯嗎?如果是這樣,wordsInDoc會在循環外部懸掛嗎?無論如何,它有可能被吊起來嗎?並可能還有其他一些可能的編譯途徑...... – Matt 2011-05-24 19:10:56

回答

3

那麼你肯定在浪費內存 - 你正在分配所有的「子陣列」,然後覆蓋它們。你會更好:

while(all documents are not read) { 
    char[][] wordsInDoc = new char[numOfWords][]; 
    for(int i=0; i < numWordsInDoc; i++) { 
     wordsInDoc[i] = getNextWord(); 
    } 
    processWords(wordsInDoc); 
} 

現在processWords實際上做什麼?如果它沒有任何地方藏匿的數組,你可以重用它:

char[][] wordsInDoc = new char[numOfWords][]; 
while(all documents are not read) { 
    for(int i=0; i < numWordsInDoc; i++) { 
     wordsInDoc[i] = getNextWord(); 
    } 
    processWords(wordsInDoc); 
} 

我肯定會執行第一個變化,但可能不第二。

至於何時發生垃圾收集 - 這是特定於實現的。

+0

@kprotocol:如果正在讀入,處理和寫出的文字很快,他們可能永遠不會超過第一代,所以在GC命中方面不會太差。如果'getNextWord'已經分配了一個char數組,那麼它有它自己的命中。現在,如果您可以重新使用所有這些數組(也許使用Unicode空字符來指示單詞的結尾),您可能幾乎可以完全消除分配。另一方面,你是否有證據表明這是一個重大的性能瓶頸? (續) – 2011-05-25 06:21:46

+0

我通常首先編寫*最簡單的*代碼,然後對其進行分析以確定優化的位置。如果你有一個實際的負載來首先測試 - 並且具體的性能需求(不僅僅是「儘可能快」),這樣可以幫助你知道什麼時候完成。 – 2011-05-25 06:22:16

4

一般來說,回答你的問題是不可能的,因爲JVM幾乎可以在垃圾回收方面做任何事情。

您可以通過在內存分析器(例如YourKit)下運行程序來了解實際發生的情況。這還將使您能夠比較不同的策略(例如,使用String類而不是char陣列),以便在內存使用情況和垃圾收集器上花費的時間。

0

垃圾收集器以神祕的方式工作。即使直接調用它也只是一個建議。

如果您想了解某個對象何時被垃圾收集,您可以覆蓋finalize()並記錄該時間的輸出信息。

1

它很可能是你正在創建數組,你立即銷燬。更有效的方法是創建數組的簡單數組,或者使用List。

char[][] wordsInDoc = new char[numOfWords][]; 
for(int i=0; i<numWordsInDoc; i++) { 
    wordsInDoc[i] = getNextWord(); 
} 
processWords(wordsInDoc); 

OR

List<char[]> wordsInDoc = new ArrayList<char[]>(); 
for(int i=0; i<numWordsInDoc; i++) { 
    wordsInDoc.add(getNextWord()); 
} 
processWords(wordsInDoc); 

或使用字符串

String line = "Hello World. This is a Sentence"; 
String[] words = line.split(" +"); 
processWords(words); 
0

我幾毛錢:)

  1. 我想,當你聲明一個數組,不像在C/C++您實際上並沒有爲這個對象保留內存,但是你簡單地創建了很多內存分配辦法。
  2. 每個引用可能會佔用一定的內存(這實際上比它指向的對象佔用的內存少)。因此,如果使用普通數組或ArrayList(它們以類型安全的方式執行相同的操作),應該沒有關係。
  3. 提到的方法的最基本的問題是,它將整個文檔加載到內存中併發送它進行處理。
  4. 更好/有效的方式將其流出(緩衝),然後即時處理它。這將阻止整個文檔被加載到內存中。

關於GC,正如人們在這裏指出的那樣,它是不可能預測的。每當JVM運行時內存不足時,它就會啓動,但這只是一句陳詞濫調:)。