2015-04-21 3 views
0

Примечание: я просмотрел все темы этой проблемы, и я понимаю, что это часто зависит от настроек 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 ГБ ОЗУ, он тратит много времени на сбор мусора, а не на обработку файла. У более сильной системы не было бы никаких проблем. Спасибо за помощь!

+0

Вы будете нуждаться, чтобы поток содержимого файла, и процесс его в кусках. Если вы используете Java 8, Stream API идеально подходит для этого. –

+0

Разве сканер уже не стример?Перед программированием я googled, какой обработчик файлов лучше обрабатывать большие файлы на Java, и многие из них рекомендуют сканер, потому что он не сохраняет весь файл в памяти. Пожалуйста, поправьте меня, если я ошибаюсь. – user134589

+0

Взгляните на [Files.lines] (https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#lines-java.nio.file.Path-) - это самый эффективный способ передачи большого файла через приложение. –

ответ

1

For the JVM arguments in Eclipse run configuration

Также вы можете попробовать добавить эту опцию при запуске: -XX: -UseGCOverheadLimit

Interesting explanation of this flag and your error message here

+1

О, спасибо! Я знаю, где найти это, что решает эту часть :) – user134589

+0

У меня есть теория о вашем коде. Должен проверить это. – MikeMatusiak

+0

Вы бы попробовали BufferedReader вместо Scanner? – MikeMatusiak

Смежные вопросы