Рассмотрим, что составляет процесс Java.
У тебя есть:
- JVM (программа на C)
- Данные JNI
- Байт-коды Java
- Java-данные
Примечательно, что ВСЕ они живут в куче C (куча JVM, естественно, является частью кучи C).
В куче Java находятся просто байт-коды Java и данные Java. Но то, что также находится в куче Java, — это «свободное пространство».
Типичная (например, Sun) JVM только увеличивает свою кучу Java по мере необходимости, но никогда не сжимает ее. Как только он достигает заданного максимума (-Xmx512M), он перестает расти и обрабатывает все, что осталось. Когда эта максимальная куча исчерпана, вы получаете исключение OutOfMemory.
Что эта опция Xmx512M НЕ делает, так это ограничивает общий размер процесса. Он ограничивает только часть процесса Java Heap.
Например, у вас может быть надуманная программа на 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 и т. д.).
person
Will Hartung
schedule
17.09.2008