2011-05-24 162 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运行时内存不足时,它就会启动,但这只是一句陈词滥调:)。