У меня есть приложение Java Spring Boot. Это действительно большое приложение со множеством сервисов, которое может выполнять кучу задач. Одна из новых задач, которую я пытаюсь реализовать, - это прочитать некоторые данные из Oracle DB и отправить их через rest в какое-то внешнее приложение.

Считываемые данные довольно большие (трудно сказать, насколько они велики, они содержат геометрические объекты), и необходимо прочитать около 1,8 миллиона записей.

Чтобы справиться с этим, я использую «разбиение на страницы» как способ чтения из БД. Это означает, что я получаю последний идентификатор чтения, а затем получаю следующую страницу на основе этого (e_id> lastReadId). Размер страницы - 100 объектов.

При последнем запуске он дошел до страницы «6672» (667200 сущностей были прочитаны и отправлены во внешнее приложение). Стоит отметить, что я не храню никаких ссылок на эти объекты, просто получаю страницу, сохраняю ее в списке и отправляю через rest. При следующем запуске этот список заменяется новой страницей и так далее.

Вот график количества извлеченных объектов за 3 часа, максимум 1030 и минимум 145 сущностей.

Fetched entities per 3h

Моя проблема в том, что приложение вылетает без ошибок. В журналах я вижу, что была получена последняя страница (в данном случае это была «6672», иногда это была другая страница), а затем внезапно появляется сообщение журнала, которое регистрируется при запуске моего приложения.

Первой моей мыслью было, что у него закончилась память и он просто вылетел. Но никаких указаний на это нет. Гарантируется ли, что OutOfMemoryError будет выброшен в такой момент? Стоит ли мне посмотреть на что-нибудь еще? Может я что-то не так делаю.

< Сильный > ИЗМЕНИТЬ

Я добавляю код, чтобы вы увидели, как я выполняю эти действия

// Get first page, last read id is null
List<MyEntity> data = dataService.collectData(pageSize, null);

sendDataToExternalService(data);
while(true) {
    final String lastReadID = data.get(data.size() - 1).getId();
    data = dataService.collectData(pageSize, lastReadID);
    sendDataToExternalService(data);
}

Метод sendDataToExternalService выглядит так

restTemplate.exchange("some/url/to-external-app", HttpMethod.PUT, new HttpEntity<>(data), List.class);

RestTemplate - это org.springframework.web.client.RestTemplate .

0
mirzak 17 Сен 2018 в 08:33

2 ответа

Лучший ответ

После некоторого профилирования с использованием JProfiler я смог узнать, что Hibernate кэширует все, что было выбрано, поскольку все выполнялось за одну транзакцию. Добавление EntityManager.clear () в существующий цикл while решило проблему.

Также стоит отметить, что весь процесс был значительно ускорен.

0
mirzak 19 Сен 2018 в 06:43

Вы можете настроить JVM на создание дампа кучи при получении этой ошибки.

Чтобы настроить JVM для создания дампа кучи, добавьте параметр -XX: + HeapDumpOnOutOfMemoryError в параметры Java и перезапустите JVM. Когда возникает ошибка пространства кучи, JVM создает файл размером с настроенный максимальный размер кучи.

Проверьте это для подробностей https: // docs. bmc.com/docs/AtriumOrchestratorPlatform/77/troubleshooting-java-virtual-machine-memory-errors-329147248.html

1
Sugan 17 Сен 2018 в 05:40