OutOfMemory в repo.save () с Pagable (небольшая страница, но много всего элементов)

Проблема:

У меня есть приложение, в котором я обрабатываю измерения обычно с точками 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 и почему общее количество элементов страницы имеет какое-то значение? Я думал, что только размер страницы влияет на количество данных, загружаемых в память.

Профилирование

OutOfMemory в repo.save () с Pagable (небольшая страница, но много всего элементов)

Обновлять

  • Когда я сбрасываю состояние точки (обработано = false), обработка выполняется снова до измерения 4, а затем я наблюдаю то же поведение.

  • Я могу без проблем обновить точки в базе данных вручную.

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

Dimitar Spasovski 27.09.2018 12:24

Я попробую. Я использовал его год назад, что привело к разбивке на страницы, которые вы видите в приведенном выше коде. Мне просто интересно, почему все работало со многими записями, и после обработки некоторых данных я оказываюсь здесь, где я не могу даже сохранить одну измененную точку: - /

hb0 27.09.2018 12:39

Профилировщик на самом деле не помог: код, вызывающий OOM, - это метод com.sun.proxy. $ Proxy130.save (Iterable), а память используется ArrayList объектов, созданных из сохраняемого спящего режима, я думаю. Что мне интересно: почему там пара миллионов объектов, когда я пытаюсь сохранить только одну страницу, например. 500 объектов?

hb0 28.09.2018 11:51

Не звоните save(Collection). вызовите save(element) вместо этого в цикле for. Затем каждая x записей выполняет flush и clear на EntityManager. Это сэкономит память и повысит вашу производительность. Все, что вы сохраняете, хранится в кэше первого уровня (EntityManager), чем больше вы добавляете, тем большим он становится. Это также повлияет на производительность, поскольку каждый раз, когда вы сохраняете что-то в спящем режиме, выполняет грязную проверку всего в кеше первого уровня, чтобы увидеть, нужно ли что-то сбрасывать с помощью БД. Еще один совет: вместо Page верните Stream, так как это будет лениво загружать записи из БД.

M. Deinum 01.10.2018 10:10

@ 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 } }

hb0 01.10.2018 10:43

Я не пробовал выполнять потоковую передачу, но, вероятно, в ближайшем будущем перейду на стек потоковой обработки / больших данных. Однако здесь задается вопрос, почему общее количество элементов небольшой страницы вообще может привести к OOM. (Я обновил вопрос, чтобы прояснить это.) В любом случае спасибо за предложение - может быть полезно для других

hb0 01.10.2018 12:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
151
0

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