Рассмотрим, из чего состоит процесс Java.
У вас есть:
- JVM (программа на C)
- Данные JNI
- байт-коды Java
- Данные Java
Примечательно, что они ВСЕ живут в куче C (куча JVM, естественно, является частью кучи C).
В куче Java есть просто байтовые коды Java и данные Java. Но в куче Java также есть «свободное место».
Типичная (т. Е. Sun) JVM только увеличивает Java Heap по мере необходимости, но никогда не сжимает ее. Как только он достигает своего определенного максимума (-Xmx512M), он перестает расти и имеет дело с тем, что осталось. Когда эта максимальная куча исчерпана, вы получаете исключение OutOfMemory.
То, что эта опция Xmx512M НЕ ДЕЛАЕТ, является ограничением общего размера процесса. Он ограничивает только часть кучи Java процесса.
Например, у вас может быть изобретенная Java-программа, которая использует 10 Мб кучи Java, но вызывает вызов JNI, который выделяет 500 Мб кучи C. Вы можете видеть, насколько велик размер вашего процесса, хотя куча Java мала. Кроме того, с помощью новых библиотек NIO вы также можете подключать память вне кучи.
Другим аспектом, который вы должны учитывать, является то, что Java GC обычно является «Копирующим коллектором». Это означает, что он берет «живые» данные из памяти, которую они собирают, и копирует их в другой раздел памяти. Это пустое пространство, которое копируется в НЕ ЧАСТЬ КУДА, по крайней мере, не с точки зрения параметра Xmx. Это, как «новая куча», и становится частью кучи после копирования (старое пространство используется для следующего GC). Если у вас куча 512 МБ, а она на 510 МБ, Java собирается куда-то скопировать живые данные. Наивная мысль была бы о другом большом открытом пространстве (например, 500+ МБ). Если бы все ваши данные были «живыми», то для копирования потребовался бы такой большой кусок.
Итак, вы можете видеть, что в самом крайнем случае вам необходимо как минимум удвоить объем свободной памяти в вашей системе для обработки определенного размера кучи. Как минимум 1 ГБ для кучи 512 МБ.
Оказывается, что на практике это не так, и выделение памяти и тому подобное сложнее, но вам необходим большой кусок свободной памяти для обработки копий кучи, а это влияет на общий размер процесса.
Наконец, обратите внимание, что JVM делает забавные вещи, такие как сопоставление классов rt.jar с виртуальной машиной, чтобы упростить запуск. Они отображаются в блоке только для чтения и могут использоваться другими процессами Java. Эти общие страницы будут «считаться» со всеми процессами Java, хотя на самом деле они потребляют физическую память только один раз (магия виртуальной памяти).
Теперь о том, почему ваш процесс продолжает расти, если вы никогда не обращаетесь к сообщению Java OOM, это означает, что ваша утечка НЕ находится в куче Java, но это не значит, что она не может быть в другом месте (JRE среда выполнения, библиотека JNI стороннего производителя, собственный драйвер JDBC и т. д.).