Сбор обрывки информации (что удивительно трудно, так как official documentation очень плохо), я определяется ...
Есть в основном две причины, это может произойти, как связанные с фрагментацией свободного пространство (т. е. свободное пространство, существующее в небольших кусках, такое, что большой объект не может быть выделен). Во-первых, сборщик мусора может не делать уплотнения, то есть он не дефрагментирует память. Даже коллекционер, который делает уплотнение, может не делать это отлично. Во-вторых, сборщик мусора обычно разбивает область памяти на регионы, которые она резервирует для разных объектов, и может не думать о том, чтобы освободить память из региона, который он должен предоставить региону, который в ней нуждается.
Сборщик мусора CMS не выполняет уплотнения, в то время как другие (серийные, параллельные, параллельные и G1). Сборщик по умолчанию в Java 8 - ParallelOld.
Все сборщики мусора разделяют память на регионы, и, AFAIK, все они слишком ленивы, чтобы очень стараться предотвратить ошибку OOM. Опция командной строки -XX:+PrintGCDetails
очень полезна для некоторых из сборщиков, показывающих размеры регионов и сколько свободного места у них есть.
Можно экспериментировать с различными сборщиками мусора и настройками. Что касается моего вопроса, сборщик G1
(с флагом JVM -XX:+UseG1GC
) решил проблему, с которой я столкнулся. Тем не менее, это было в основном до шанса (в других ситуациях, это OOM быстрее). Некоторые из сборщиков (серийный, cms и G1) имеют широкие возможности настройки для выбора размеров различных регионов, чтобы вы могли бесполезно тратить время, пытаясь решить проблему.
В конечном счете, реальные решения довольно неприятны. Во-первых, нужно установить больше оперативной памяти. Во-вторых, это использование меньших массивов. В-третьих, следует использовать ByteBuffer.allocateDirect
. Прямые байтовые буферы (и их int/float/double wrappers) являются подобными массиву объектами с массивной производительностью, которые выделяются в родной куче ОС. Куча ОС использует аппаратное обеспечение виртуальной памяти процессора и не имеет проблем с фрагментацией и даже может эффективно использовать пространство подкачки диска (позволяющее выделять больше памяти, чем доступная оперативная память). Однако большой недостаток заключается в том, что JVM действительно не знает, когда следует освобождать прямые буферы, делая этот вариант более желательным для долгоживущих объектов. Конечным, возможно лучшим и, безусловно, самым неприятным вариантом является выделение и освобождение памяти изначально с использованием вызовов JNI и использование его в Java путем его упаковки в ByteBuffer
.
Имеет ли ваша виртуальная машина вашего хоста столько RAM? Пример: ваша виртуальная машина (ПК) вашего хоста имеет 4 ГБ памяти, но вы даете гостевой VM 10 ГБ памяти. Я не знаю, будет ли пейджинг, но мне просто интересно узнать характеристики вашей хост-системы. –
Моя машина имеет 10 ГБ оперативной памяти плюс 10 ГБ виртуальной памяти. –