Как решить Недостаточно памяти, если ваша процедура возвращает большое количество записей

В настоящее время я работаю над приложением Java. Используемая база данных - Sybase ASE 15.0. В настоящее время я получаю следующую ошибку в одном из сценариев:

java.lang.OutOfMemoryError: Java heap space

У меня есть следующий вызов процедуры в моем коде:

CallableStatement cStmt = null;
ResultSet rs = null;
ArrayList list = new ArrayList();
Connection con = getConnection();
cStmt = con.prepareCall("call <proc-name>(?,?)", ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
boolean b;
// .....
// .....
while(rs.next()){
  List dList = new ArrayList();
  T t = new T();
  t.setProp1(rs.getBoolean("isAvailable"));
  t.setProp2(rs.getString("Name"));
  t.setDList(dList);
  ....
  ....
  t.setPropN(rs.getString("Property-name"));//many properties are read
  //  from db and POJO is populated.


  b = true;
  int Id = rs.getInt("Id");

  do{
     D d = new D();
     d.setProp1(rs.getFloat("<property-name"));
     ....
     ....
     d.setPropN(rs.getString("Property-name"));//many properties are read      
     dList.add(d);

     if(rs.next()){

         if(rs.getInt("Id")!=Id){
              b = false;
              rs.previous();    
         }

     } else {
             b = false;
             rs.previous();
     }
  } while (b);

  list.add(t);
}

Процедура получает диапазон дат в качестве аргументов. Если диапазон дат большой, он возвращает большое количество записей. Для небольшого количества записей он работает нормально, но для больших записей он дает исключение "недостаточно памяти". Я заметил, что если количество записей больше 11997, то это выдает эту ошибку.

Я прочитал, что простой набор результатов хранит записи в динамической памяти, но он должен работать с прокручиваемым набором результатов, верно?

Как мне удалить это исключение в этом случае.

3 ответа

Почему бы не использовать ограничения? Если вы не можете ограничить диапазон дат, который может привести к огромному набору данных, вы должны использовать ограничения, т.е. вы делаете что-то с первыми 100, затем со следующими 100 и т. Д. Тогда у вас есть только текущие 100 в памяти.

Сначала просто ресурс чистоты: пользуйся try (ResultSet rs = cStmt.query()) { ... } чтобы закрыть набор результатов впоследствии.

Sвыдал результат хранимой процедуры во (временную) таблицу и использовал SELECT, возможно, разбитый на страницы. Затем набор результатов может повторяться. Вызов, вероятно, передает все данные в JDBC. Я не знаю возможностей хранимых процедур Sybase, но, возможно, вы можете там что-то делать.

Проверьте, используйте VARCHAR, а не CHAR или что-то подобное.

Используйте UTF-8, а не UTF-16LE.

При хранении в java, возможно, кэшируйте равные Strings/BigDecimals в карте идентичности, чтобы в данных использовался только первый экземпляр объекта.

Как насчет того, чтобы просто увеличить максимальную память кучи вашей виртуальной машины по умолчанию?

-Xmx<size> - to set the maximum Java heap size
-Xms<size> - to set the initial Java heap size

Вы также можете попробовать профилировщик, чтобы более точно определить утечку памяти. Вы должны быть осторожны с этим циклом while, с тем, что вы вставили в него, и с тем, что соответствует критериям GC

Проверьте эту информативную статью о стоимости создания объекта:

http://drmirror.net/2013/06/06/object-creation/

Хорошие методы эффективной работы с памятью для Java: https://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf


Обновлено: в ответ на "Есть ли другой способ, что результирующий набор не считывает все записи в память одновременно и читает их порциями? " Попробуйте использовать setFetchSize для ResultSet (rs)

Узнайте больше о размере setFetch:

/questions/18654532/resultset-zagruzhaet-vse-dannyie-v-pamyat-ili-tolko-po-zaprosu/18654544#18654544

/questions/12475597/chto-i-kogda-mne-sleduet-ukazyivat-setfetchsize/12475610#12475610

/questions/8347460/putanitsa-s-metodom-setfetchsize-obekta-statement/8347472#8347472


Обновление 2 В ответ на ваш код цикла:

объявите все ваши переменные вне циклов и инициализируйте их, как вам нужно; Итак, перед циклом:

List dList;
T t;
int Id
D d;
while(rs.next()){
  dList = new ArrayList();
  t = new T();
  // and so on...

  list.add(t);
}

А потом попробуйте еще раз.

Другие вопросы по тегам