Проблема:
У меня есть приложение, в котором я обрабатываю измерения обычно с точками 60-6 м на регулярной основе уже пару лет.
Теперь у меня есть измерение с ~ 43 миллионами точек. Попытка изменить состояние хотя бы одной точки приводит к исключению OutOfMemory..
Обработка выполняется для 3 измерений (содержащих ~ 60 тыс. Точек) и разбивается на 4-е, очень большое измерение: метод save () работает бесконечно, пока я не получу OOM.
Куча
Sprint Boot 1.5.16.RELEASE с Hibernate и Postgres 9.5.
Упрощенный код
// pageSize worked with 300k before but for debugging: 500
Page<Point> page = getNextPage(m, pageSize);
ResultCache result = process(page);
// This is the cause of the OOM, even with a single point!
repo.save(result.getChangedPoints());
// save(one single element) also ends up in the OOM so I cannot execut code afterwards like flush
private Page<Point> getNextPage(Measurement m, int pageSize) {
return repo.findByMeasurement(m, new PageRequest(0, pageSize));
}
@Repository
public interface PointRepository extends
JpaRepository<Point, Long> {}
Отладка
Когда я отлаживаю в своей среде IDE, я достигаю первого оператора repo.save(), но как только я выполняю этот оператор, память заполняется до тех пор, пока не достигнет ~ 4 ГБ, и через несколько минут я получаю сообщение OOME.
Вопрос
Как я могу избежать OOM и почему общее количество элементов страницы имеет какое-то значение? Я думал, что только размер страницы влияет на количество данных, загружаемых в память.
Профилирование
Обновлять
Когда я сбрасываю состояние точки (обработано = false), обработка выполняется снова до измерения 4, а затем я наблюдаю то же поведение.
Я могу без проблем обновить точки в базе данных вручную.
Я попробую. Я использовал его год назад, что привело к разбивке на страницы, которые вы видите в приведенном выше коде. Мне просто интересно, почему все работало со многими записями, и после обработки некоторых данных я оказываюсь здесь, где я не могу даже сохранить одну измененную точку: - /
Профилировщик на самом деле не помог: код, вызывающий OOM, - это метод com.sun.proxy. $ Proxy130.save (Iterable), а память используется ArrayList объектов, созданных из сохраняемого спящего режима, я думаю. Что мне интересно: почему там пара миллионов объектов, когда я пытаюсь сохранить только одну страницу, например. 500 объектов?
Не звоните save(Collection). вызовите save(element) вместо этого в цикле for. Затем каждая x записей выполняет flush и clear на EntityManager. Это сэкономит память и повысит вашу производительность. Все, что вы сохраняете, хранится в кэше первого уровня (EntityManager), чем больше вы добавляете, тем большим он становится. Это также повлияет на производительность, поскольку каждый раз, когда вы сохраняете что-то в спящем режиме, выполняет грязную проверку всего в кеше первого уровня, чтобы увидеть, нужно ли что-то сбрасывать с помощью БД. Еще один совет: вместо Page верните Stream, так как это будет лениво загружать записи из БД.
@ M.Deinum Проблема с flush (): я не могу до него добраться, так как сохранение (отдельный элемент) не завершается и попадает в OOM. final ArrayList<Point> elements = new ArrayList<>(result.getChangedPoints()); for (int i = 0; i < elements.size(); i++) { repo.save(elements.get(i)); // executing this once ends up in the OOM and does not finishif (i % 1000 == 0) { repo.flush(); //repo.clear(); this does not exist in the JpaRepository interface by default - see code in updated Question } }
Я не пробовал выполнять потоковую передачу, но, вероятно, в ближайшем будущем перейду на стек потоковой обработки / больших данных. Однако здесь задается вопрос, почему общее количество элементов небольшой страницы вообще может привести к OOM. (Я обновил вопрос, чтобы прояснить это.) В любом случае спасибо за предложение - может быть полезно для других





Рассматривали ли вы использование профилировщиков, чтобы увидеть данные, вызывающие исключение OOM? Например, jprofiler - один из лучших, что я использовал. ej-technologies.com/products/jprofiler/overview.html Это не бесплатно, но у него есть пробная версия, которую вы можете использовать для поиска утечки памяти.