У меня есть приложение Java Spring Boot. Это действительно большое приложение с множеством сервисов, которое может выполнять кучу задач. Одна из новых задач, которую я пытаюсь реализовать, - это прочитать некоторые данные из Oracle DB и отправить их через rest в какое-то внешнее приложение.
Считываемые данные довольно большие (трудно сказать, насколько они велики, они содержат геометрические объекты), и необходимо прочитать около 1,8 миллиона записей.
Чтобы справиться с этим, я использую «разбиение на страницы» как способ чтения из БД. Это означает, что я получаю последний идентификатор чтения, а затем на его основе получаю следующую страницу (e_id> lastReadId). Размер страницы 100 объектов.
При последнем запуске он дошел до страницы «6672» (667200 сущностей были прочитаны и отправлены во внешнее приложение). Стоит отметить, что я не храню никаких ссылок на эти объекты, просто получаю страницу, сохраняю ее в списке и отправляю через rest. При следующем запуске этот список заменяется новой страницей и так далее.
Вот график количества извлеченных сущностей за 3 часа, максимум 1030 и минимум 145 сущностей.
Моя проблема в том, что приложение вылетает без ошибок. В журналах я вижу, что была получена последняя страница (в данном случае это была «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
Похоже на проблему с утечкой памяти. Вы пробовали анализировать это с помощью такого инструмента, как VisualVM?
Это было запущено на одном из наших серверов, теперь я пытаюсь настроить все локально на моем ПК, запустить его с помощью VisualVM и попытаться посмотреть, что произойдет.
Когда базовая ОС обнаруживает блокировки или чрезмерное использование памяти, она может просто завершить процесс. Это без того, чтобы вы видели исключения OOM и т. д., Поэтому это зависит от того, что убивает процесс. Кроме того, если у вас много результатов, вы должны передавать результат в потоковом режиме, а не буферизовать его все на клиенте. Добавьте код к вашему вопросу, чтобы увидеть, как вы создаете запрос и читаете результаты.




Вы можете настроить JVM на создание дампа кучи при получении этой ошибки.
Чтобы настроить JVM для создания дампа кучи, добавьте параметр -XX: + HeapDumpOnOutOfMemoryError в параметры Java и перезапустите JVM. Когда возникает ошибка пространства кучи, JVM создает файл размером с настроенный максимальный размер кучи.
Проверьте это для подробностей https://docs.bmc.com/docs/AtriumOrchestratorPlatform/77/troubleshooting-java-virtual-machine-memory-errors-329147248.html
После некоторого профилирования с использованием JProfiler я смог узнать, что Hibernate кэширует все, что было выбрано, поскольку все выполнялось за одну транзакцию. Добавление EntityManager.clear () в существующий цикл while решило проблему.
Также стоит отметить, что весь процесс был значительно ускорен.
Думаю, лучше будет, если вы загрузите их частично, а не загрузите огромное количество записей? После обработки можно загружать дальше?