Вам необходимо понять главный факт об эргономике сборщика мусора:
Дорогостоящей частью сборки мусора является поиск и работа с объектами, которые НЕ являются мусором.
Это означает, что когда куча приближается к своей максимальной емкости, сборщик мусора будет тратить все больше и больше времени на все меньше и меньше возврата в освобожденном пространстве. Если GC попытается использовать каждый последний байт памяти, результатом будет то, что ваша JVM будет тратить все больше и больше времени на сбор мусора, пока ... в конце концов ... практически не будет сделано никакой полезной работы.
Чтобы избежать этой патологической ситуации, JVM следит за соотношением времени, затраченного на сбор и выполнение полезной работы. Когда отношение превышает настраиваемое пороговое значение, ГХ поднимает OutOfMemoryError
... даже при том, что (технически) имеется свободная память. Это, вероятно, то, что вы видите, хотя другие объяснения в равной степени правдоподобны.
Вы можете изменить пороги GC, размеры генерации и т. Д. С помощью параметров JVM, но, вероятно, лучше этого не делать. Лучшая идея - выяснить, почему использование памяти вашим приложением постоянно увеличивается. Наиболее вероятные утечки памяти ... то есть ошибки ... в вашем коде, которые вызывают это. Потратьте свои усилия на поиск и исправление этих ошибок, не беспокоясь о том, почему вы не используете всю память.
(На самом деле, вы используете его ... но не всегда.)