Производительность сборщика мусора Java для выделения / освобождения памяти внутри цикла - PullRequest
1 голос
/ 24 мая 2011

У меня есть программа, в которой рассматриваемый цикл выглядит примерно так

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);
}

Мне было интересно, что происходит за сценой, когда этот цикл выполняется. Когда сборщик мусора собирает память, выделенную для каждого документа? Это лучший способ (по использованию памяти) сделать то же самое?

Любое понимание приветствуется.

Ответы [ 5 ]

4 голосов
/ 24 мая 2011

Невозможно ответить на ваш вопрос в целом, поскольку JVM может делать все, что захочет, в отношении сбора мусора.

Вы можете получить некоторое представление о том, что на самом деле происходит, запустив вашу программу под профилировщиком памяти, таким как YourKit . Это также позволит вам сравнивать различные стратегии (например, используя класс String вместо char массивов) с точки зрения использования памяти и времени, проведенного в сборщике мусора.

3 голосов
/ 24 мая 2011

Ну, вы определенно тратите впустую память - вы выделяете все «подмассивы», а затем перезаписываете их. Вам было бы лучше с:

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);
}

Я бы определенно выполнил первое изменение, но, вероятно, не второе.

Что касается того, когда именно происходит сборка мусора - это зависит от реализации.

1 голос
/ 24 мая 2011

Вероятно, вы создаете массив, который немедленно уничтожаете. Более эффективный подход заключается в создании простого массива массивов или использовании списка.

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 голосов
/ 24 мая 2011

Мои несколько центов:)

  1. Полагаю, когда вы объявляете массив, в отличие от C / C ++, вы фактически не резервируете память для объекта, а просто создаете столько ссылок.
  2. Каждая ссылка может занимать определенную память (которая, вероятно, будет меньше, чем память, занятая объектом, на который она указывает).Поэтому не должно иметь значения, используете ли вы обычный массив или ArrayList (которые делают то же самое, но безопасным для типов способом).
  3. Самая основная проблема с упомянутым подходом состоит в том, что он загружает весь документ в память иотправляет его на обработку.
  4. Лучший / эффективный способ потоковой передачи (буферизованный) и последующей обработки на лету.Это предотвратит загрузку всего документа в память.

Относительно GC, как указывали здесь люди, предсказать невозможно.Он запускается всякий раз, когда JVM испытывает недостаток памяти, но это просто предложение клише:).

0 голосов
/ 24 мая 2011

Сборщик мусора работает таинственным образом. Даже прямое обращение к нему приводит лишь к предложению.

Если вы хотите узнать, когда происходит сборка мусора, вы можете переопределить finalize() и записать информацию о времени.

...