Примечание: я просмотрел все темы этой проблемы, и я понимаю, что это часто зависит от настроек JVM и эффективного кодирования, но я не знаю, как улучшить еще больше.Java OutOfMemoryError: превышение верхнего предела GC при обработке большого текстового файла - невозможно определить, как повысить производительность
Я обрабатываю большой текстовый файл (1 ГБ) сетевых топологий CAIDA, это в основном свалка всей топологии интернет-IPv4. Каждая строка имеет формат «долгота долготы города страны континентов», и мне нужно отфильтровать все повторяющиеся узлы (например, каждый узел с той же широтой/долготой).
Я назначаю уникальное имя всем узлам с тем же географическим местоположением и сохраняю хэш-карту каждого геосостояния -> уникальное имя, которое уже встречается. Я также сохраняю хэш-файл каждого имени_стое-> уникальное имя, потому что на следующем шаге я должен обработать другой файл, в котором эти старые имена должны быть сопоставлены с новым уникальным именем для каждого местоположения.
Я написал это на Java, потому что именно здесь происходит вся моя другая обработка, но я получаю ошибку «превышение лимита по протоколу GC». Ниже мой код, который выполняется, и журнал регистрации ошибок:
Scanner sc = new Scanner(new File(geo));
String line = null;
HashMap<String, String> nodeGeoMapper = new HashMap<String, String>(); // maps each coordinate to a unique node name
HashMap<String, String> nodeMapper = new HashMap<String, String>(); // maps each original node name to a filtered node name (1 name per geo coordinate)
PrintWriter output = new PrintWriter(geoFiltered);
output.println("#node.geo Name\tcontintent\tCountry\tregion\tcity\tlatitude\tlongitude");
int frenchCounter = 0;
// declare all variables used in loop to avoid creating thousands of tiny objects
String[] fields = null;
String name = null;
String continent = null;
String country = null;
String region = null;
String city = null;
double latitude = 0.0;
double longitude = 0.0;
String key = null;
boolean seenBefore = true;
String newname = null;
String nodename = null;
while (sc.hasNextLine()) {
line = sc.nextLine();
if (line.startsWith("node.geo")) {
// process a line and retrieve the fields
fields = line.split("\t"); // split all fields using the space as separator
name = fields[0];
name = name.trim().split(" ")[1]; // nodes.geo' 'N...
continent = ""; // is empty and gets skipped
country = fields[2];
region = fields[3];
city = fields[4];
latitude = Double.parseDouble(fields[5]);
longitude = Double.parseDouble(fields[6]);
// we only want one node for each coordinate pair so we map to a unique name
key = makeGeoKey(latitude, longitude);
// check if we have seen a node with these coordinates before
seenBefore = true;
if (!nodeGeoMapper.containsKey(key)) {
newname = "N"+nodeCounter;
nodeCounter++;
nodeGeoMapper.put(key, newname);
seenBefore = false;
if (country.equals("FR"))
frenchCounter++;
}
nodename = nodeGeoMapper.get(key); // retrieve the unique name assigned to these geo coordinates
nodeMapper.put(name, nodename); // keep a reference from old name to new name so we can map later
if (!seenBefore) {
// System.out.println("node.geo "+nodename+"\t"+continent+"\t"+country+"\t"+region+"\t"+city+"\t"+latitude+"\t"+longitude);
output.println("node.geo "+nodename+"\t"+continent+"\t"+country+"\t"+region+"\t"+city+"\t"+latitude+"\t"+longitude);
}
}
}
sc.close();
output.close();
nodeGeoMapper = null;
Ошибка:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.regex.Matcher.<init>(Unknown Source)
at java.util.regex.Matcher.toMatchResult(Unknown Source)
at java.util.Scanner.match(Unknown Source)
at java.util.Scanner.hasNextLine(Unknown Source)
at DataProcessing.filterGeoNodes(DataProcessing.java:236)
at DataProcessing.main(DataProcessing.java:114)
В процессе выполнения мой процесс Java постоянно работает на 80% CPU в общей сложности 1,000,000K (примерно) памяти (ноутбук имеет всего 4 ГБ). Выходной файл получил до 59987 уникальных узлов, поэтому это количество ключевых значений в GehLocation-> Name hashmap. Я не знаю размер hashmap oldName-> NewName, но это должно быть меньше Integer.Max_value, потому что в моем текстовом файле не так много строк.
Мои два вопроса:
как я могу улучшить свой код, чтобы использовать меньше памяти или избежать столько GC? (Редактирование: пожалуйста, держите его совместимым с Java 7)
(решена) Я прочитал потоки с настройками JVM, такими как -Xmx1024m, но я не знаю, где в Eclipse IDE я могу изменить эти параметры. Может кто-нибудь, пожалуйста, покажите мне, где мне нужно установить эти настройки и какие настройки я могу попробовать?
Спасибо
РЕШИТЬ: для людей с подобной проблемой, вопрос был nodeMapper HashMap, который должен был хранить 34 миллионов объектов String, в результате которых более 4 Гб оперативной памяти, необходимой. Я смог запустить свою программу, сначала отключив порог GC -XX: -UseGCOverheadLimit, а затем выделив 4GBRAM для моего Java-процесса, используя -Xmx4gb. Потребовалось много времени, чтобы обработать его, но он действительно работал, это было просто медленно, потому что, как только Java достигает 3-4 ГБ ОЗУ, он тратит много времени на сбор мусора, а не на обработку файла. У более сильной системы не было бы никаких проблем. Спасибо за помощь!
Вы будете нуждаться, чтобы поток содержимого файла, и процесс его в кусках. Если вы используете Java 8, Stream API идеально подходит для этого. –
Разве сканер уже не стример?Перед программированием я googled, какой обработчик файлов лучше обрабатывать большие файлы на Java, и многие из них рекомендуют сканер, потому что он не сохраняет весь файл в памяти. Пожалуйста, поправьте меня, если я ошибаюсь. – user134589
Взгляните на [Files.lines] (https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#lines-java.nio.file.Path-) - это самый эффективный способ передачи большого файла через приложение. –