Приложение Java 8, использующее всю оперативную память системы, а затем аварийно завершающее работу с SIGBUS. Что тут происходит?

У меня есть приложение Java 8, которое принимает сообщения по сети и записывает в несколько файлов с отображением памяти, используя Java NIO MappedByteBuffer. У меня есть ридер, который одновременно читает сообщения из этих файлов по порядку и снова удаляет прочитанные файлы с помощью MappedByteBuffer. Все гладко, пока я не написал и не прочитал около 246 ГБ данных, и мое приложение не вылетает со следующим

[thread 139611281577728 also had an error][thread 139611278419712 also had an error][thread 139611282630400 also had an error][thread 139611277367040 also had an error][thread 139611283683072 also had an error][thread 139611279472384 also had an error]





[thread 139611280525056 also had an error]
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0x7) at pc=0x00007f02d10526de, pid=44460, tid=0x00007ef9c9088700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_101-b13) (build 1.8.0_101-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode linux-amd64 )
# Problematic frame:
# v  ~StubRoutines::jint_disjoint_arraycopy
#
# Core dump written. Default location: /home/user/core or core.44460
#
# An error report file with more information is saved as:
# /home/user/hs_err_pid44460.log

hs_err_pid44460.log пуст, а дамп ядра core.44460 имеет размер около 246 ГБ и полон сообщений, которые я пытаюсь написать.

Я работаю с максимальным размером кучи 32 ГБ. Согласно JConsole, у меня заканчивается Free Physical Memory, а затем происходит сбой. Снимок экрана JConsole

Почему у меня заканчивается оперативная память? Я забыл закрыть какой-то дескриптор файла / неправильно закрыл файлы MMapped?


person Chinmay Nerurkar    schedule 27.08.2016    source источник


Ответы (3)


Несмотря на то, что ваша программа действительно может быть правильной в использовании MappedByteBuffers, обратите внимание, что при высокой скорости выделения вы можете столкнуться с явлениями из-за несвоевременного освобождения указанных буферов, что в конечном итоге является обязанностью JVM и должно происходить только во время сборки мусора буферов. . Короче говоря, освобождение буфера в конечном итоге будет успешным, но когда это произойдет, трудно предсказать.

Однако вы можете принудительно освободить («очистить») память, выделенную для буферов, с помощью функции JVM Cleaner (класс sun.misc.Cleaner). Пожалуйста, обратитесь к этому вопросу SO для некоторых направлений, но, короче говоря, вам просто следует вызывать Cleaner.clean() для одноразовых буферов как можно раньше, чтобы уменьшить показатели выделения памяти и эффективно поддерживать ваш вариант использования.

person logtwo    schedule 29.08.2016
comment
Решение состояло в том, чтобы использовать Cleaner для выполнения работы. В частности, это помогло -› stackoverflow.com/a/19447758/1041963 - person Chinmay Nerurkar; 06.09.2016
comment
обратите внимание, что неправильное использование очистителя может привести к сбоям и является внутренним API, который может меняться от версии к версии. - person the8472; 07.09.2016
comment
@ the8472, вы правы, особенно в отношении изменения API. Я постараюсь как можно скорее обновить свой ответ некоторыми указаниями для Java9, который начинает прояснять вопрос о внутренних (но широко используемых!) API, см. Также: openjdk.java.net/jeps/260 - person logtwo; 08.09.2016

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

Если он действительно дает сбой из-за отображенных или прямых буферов, то вы либо пропускаете их (выполнение дампов кучи через анализатор памяти может их идентифицировать), либо сборщик мусора работает слишком редко, чтобы освободить их.

person the8472    schedule 28.08.2016
comment
Запуск GC, кажется, помогает. Хотя G1 GC не запускает коллекцию G1 Old Generation достаточно часто (что, кажется, помогает). - person Chinmay Nerurkar; 07.09.2016
comment
в этом случае может помочь настройка GC для более частого запуска. - person the8472; 07.09.2016

Вам также может помочь более агрессивная сборка мусора.

Также можно попробовать эту опцию, которая была введена в J7: -XX:+UseG1GC -XX:ParallelGCThreads=4 Это позволит использовать 4 потока для GC параллельно

Есть ряд хороших статей о дополнительной настройке сборщика мусора, вот одна из них, которую я нашел полезной (https://blogs.oracle.com/java-platform-group/entry/g1_from_garbage_collector_to)

Надеюсь это поможет.

person Stuart Ledwich    schedule 15.09.2016