Я пытаюсь прочитать большой текстовый корпус в памяти с помощью Java. В какой-то момент он попадает в стену, и только мусор собирается бесконечно. Я хотел бы знать, есть ли у кого-либо опыт избиения GC Java в представлении с большими наборами данных.Плохая производительность с большими списками Java
Я читаю 8-гигабайтный файл английского текста в UTF-8 с одним предложением к строке. Я хочу split()
каждую строку в пробеле и хранить полученные массивы String в ArrayList<String[]>
для дальнейшей обработки. Вот упрощенная программа, которая проявляет проблему:
/** Load whitespace-delimited tokens from stdin into memory. */
public class LoadTokens {
private static final int INITIAL_SENTENCES = 66000000;
public static void main(String[] args) throws IOException {
List<String[]> sentences = new ArrayList<String[]>(INITIAL_SENTENCES);
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
long numTokens = 0;
String line;
while ((line = stdin.readLine()) != null) {
String[] sentence = line.split("\\s+");
if (sentence.length > 0) {
sentences.add(sentence);
numTokens += sentence.length;
}
}
System.out.println("Read " + sentences.size() + " sentences, " + numTokens + " tokens.");
}
}
Похоже, что вырезано-высушено, правильно? Вы заметите, что я даже сделал предварительный размер ArrayList
; У меня чуть меньше 66 миллионов предложений и 1,3 миллиарда жетонов. Теперь, если вы выскочить ваш Java object sizes ссылку и карандаш, вы обнаружите, что должно требовать около:
- 66e6
String[]
ссылки @ 8 байт еа = 0,5 GB - 66e6
String[]
объекты @ 32 байт еа = 2 ГБ - 66e6
char[]
объекты @ 32 байта еа = 2 Гб - 1.3e9
String
ссылки @ 8 байт еа = 10 ГБ - 1.3e9
String
с @ 44 байт еа = 53 Гб - 8e9
char
с @ 2 байта еа = 15 Гб
83 Гб. (Вы заметите, что мне действительно нужно использовать размеры 64-битных объектов, поскольку Compressed OOPs не может помочь мне с кучей 32 ГБ.) Нам повезло, что у вас есть машина RedHat 6 со 128 ГБ оперативной памяти, поэтому я запускаю моя 64-разрядная серверная виртуальная машина Java HotSpot ™ (сборка 20.4-b02, смешанный режим) из моего набора Java SE 1.6.0_29 с pv giant-file.txt | java -Xmx96G -Xms96G LoadTokens
только для того, чтобы быть в безопасности и отбрасывать назад, пока я смотрю top
.
Где-то менее чем на полпути через вход, около 50-60 ГБ RSS, параллельный сборщик мусора запускает до 1300% CPU (16 proc box) и считывает остановки хода. Затем идет еще несколько ГБ, затем прогресс останавливается еще дольше. Он заполняет 96 ГБ и еще не завершен. Я отпустил его на полтора часа, и это просто сжигание ~ 90% времени в системе GC. Это кажется экстремальным.
Чтобы убедиться, что я не был сумасшедшим, я взломал эквивалентный Python (все две строки;), и он продлился до 12 минут и 70 ГБ RSS.
Итак: я делаю что-то немое? (Помимо неэффективного способа хранения вещей, который я действительно не могу вам помочь, и даже если мои структуры данных толстые, пока они подходят, Java не должна просто задыхаться.) Есть ли волшебство Рекомендации GC для действительно больших куч? Я попробовал -XX:+UseParNewGC
, и это кажется еще хуже.
Где хранятся объекты 'char []', поддерживающие строки? –
В объектах 'String': 24-байтовый заголовок объекта + 8 байтов' char [] 'указатель + 4 байта start, offset и hashcode, если мои вычисления верны. –
Это 'char []' * reference * - но как насчет 'char []' * objects * самих? У массива 'char []' тоже есть надстройка объекта ... –