2016-08-06 3 views
0

У меня есть Java-программа для веб-сервисов, которая читает 13 000 000 дат типа '08 -23-2016 12:54:44 'как строки из базы данных. Моей развивающейся средой являются Java 8, MySQL 5.7 и tomcat 8. Я объявляю строковый массив String [] для его сохранения. Я использую Guice для ввода начальных значений массива данных в пустые. Однако использование памяти по-прежнему огромно. Это мой код:Уменьшение использования памяти в java

String[] data;//size is 1,000,000 
void generateDataWrapper(String params) { 
     //read over 13000000 dates string 
     ResultSet rs = mySQLCon.readData(params); 
     clearData(data);//set to empty string 
     int index = 0; 
     while(rs.next()) { 
      data[index++] = rs.getString("date"); 
      if (index == (size - 1)) {//calculate every 1,000,000 total 13 times 
       //calculate statistics 
       ... 
       //reset all to empty string 
       clearData(data);  
       index = 0; 
      } 
     } 
} 
//mySQLCon. readData function 
ResultSet readData(String params) { 
     try { 
      String query = generateQuery(params); 
      Statement postStmt = connection.createStatement(); 
      ResultSet rs = postStmt.executeQuery(query); 
     return rs; 
     } catch (Exception e) { 
     } 
     return null; 
} 

Если я называю эту функцию один раз, память достигает 12G, если я называю его снова, память переходит на 20G, на третий раз воля памяти идет в 25G и бросить 'java.lang.OutOfMemoryError: GC предел накладных расходов превысил' ошибка в com.mysql.jdbc.MysqlIO.nextRowFast (MysqlIO.java:2174)

Это часть сообщения об ошибке:

java.lang.OutOfMemoryError: GC overhead limit exceeded 
    com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2174) 
    com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1964) 
    com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3316) 
    com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:463) 
    com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3040) 
    com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2288) 
    com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2681) 
    com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2547) 
    com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2505) 
    com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1370) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    java.lang.reflect.Method.invoke(Unknown Source) 

I изменили алгоритмы сбора мусора на: -XX: + UseConcMarkSweepGC -XX: + CMSIncr ementalMode Но это не помогает. Я попытался изменить данные на статические переменные, все еще будет иметь эту проблему. В настоящее время куча JVM составляет 8 г, память tomcat составляет 24 г, однако я не думаю, что увеличение памяти решит проблему.

Я не понимаю, почему моя память все увеличивается каждый раз, когда я называю эту функцию. Может ли кто-нибудь дать мне какое-нибудь предложение?

+0

Вы пробовали профилирование памяти? или чтение документов API, которые вы используете? в основном, что-нибудь еще, чем дать ему больше памяти? – the8472

+0

Это не полная программа, хотя я могу догадаться, что делают clearData() и размер, это просто догадки. Тем не менее, профилирование памяти, предложенное the8472, может быть вашим лучшим способом: захватить кучу дампа, когда у программы закончится память, и использовать инструмент профилирования, чтобы выяснить, кто держится за память. – Lars

+0

Вам действительно нужно использовать String для хранения даты? Используйте LocalDateTime, и вы можете использовать меньше памяти для каждого объекта. –

ответ

3
  1. Использованные ресурсы, такие как , должны быть закрыты для выпуска базовых системных ресурсов. Это можно сделать автоматически declaring the resources in a try-block, как try (ResultSet resultSet =...).

  2. Вы можете попробовать fetch only a limited number of rows from database, если они запрошены от ResultSet, и не все из них немедленно.

  3. Объекты получают право на сбор мусора, если на них больше не ссылаются. Таким образом, ваш массив-объект сохраняет в памяти весь его размер до тех пор, пока он ссылается. Если он больше не ссылается и у VM заканчивается память, он сможет распоряжаться массивом-объектом, возможно, избегая OutOfMemoryError.

  4. Неожиданно высокая загрузка памяти может быть проанализирована creating a heap dump и exploring it in the tool jvisualvm of the JDK.

1

Кроме того, вы можете изменить свой массив строк на длинный массив, поскольку строки потребляют огромное количество памяти. В вашем случае размер строки даты составляет 38 байт (19 символов * 2 байта), тогда как long занимает всего 8 байт памяти.

long[] data;//size is 1,000,000 
void generateDataWrapper(String params) { 
    //read over 13000000 dates string 
    ResultSet rs = mySQLCon.readData(params); 
    clearData(data);//set to empty string 
    int index = 0; 
    SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss"); 
    while(rs.next()) { 
     try{ 
      Date date = formater.parse(rs.getString("date")); 
      data[index++] = date.getTime(); 
     }catch(ParseException pe) { 
      pe.printStackTrace(); 
     } 
     if (index == (size - 1)) {//calculate every 1,000,000 total 13 times 
      //calculate statistics 
      ... 
      //reset all to empty string 
      clearData(data); 
      index = 0; 
     } 
    } 
} 

Везде, где вам нужен строку, вы можете просто разобрать его обратно со следующими

SimpleDateFormat formater = new SimpleDateFormat("MM-dd-YYYY HH:mm:ss"); 
Date date = new Date(data[i]); 
String dateString = formater.format(date); 
+0

Мне нравится ваша идея, когда у меня есть огромные данные или когда я ее оптимизирую, я буду делать это –

0

Во-первых, спасибо за все ваши предложения. Я понял это, прочитав от mm759 и понял, что забыл закрыть ResultSet после того, как прочитал. После того, как я добавляю rs.close(), каждый раз, когда требуется одно и то же время, хотя память достигнет максимальной памяти, которую я установил.

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