 | Уровень сложности: простой Ли Ченг, технический консультант, IBM
19.08.2009 Эта статья - вторая из цикла статей, объясняющих, как запустить Java-приложение на платформе AIX быстро и без особых помех. В статье рассматривается модель памяти Java-процесса на AIX как для 32-, так и для 64-битной версий. Автор обсуждает адресное пространство 32-битного процесса в AIX, его влияние на размер "кучи" Java, которая доступна 32-битной Java-машине, и на какие хитрости необходимо пойти, чтобы получить максимально возможный размер кучи Java для 32-битного Java-процесса. Также описывается адресное пространство 64-битного процесса и как с его помощью получить великолепную кучу Java.
Модель памяти 32-битной виртуальной машины Java на AIX
Для того чтобы понять, что такое модель памяти 32-битной виртуальной машины Java на AIX, сначала необходимо разобраться в адресном пространстве 32-битных процессов на AIX. Различные модели адресного пространства для AIX подробно рассматриваются в учебнике IBM Redbook "Developing and porting C and C++ applications on AIX", form number SG245674, и в документации AIX "General Programming Concepts: Writing and Debugging Program", глава "Large Program Support". Несмотря на то что модели адресного пространства, имеющие отношение к модели памяти Java, будут рассмотрены в этой статье, настоятельно рекомендуется ознакомиться с каждым из этих документов, особенно если в ваших Java-приложениях используется интерфейс JNI (Java Native Interface).
Стандартное адресное пространство 32-битного приложения
AIX - операционная система, использующая виртуальную память для обращения к большему объему памяти, чем доступно в системе. Трансляция действительных адресов (виртуальных, логических) в физические (реальные) для доступа к инструкциям и хранилищу данных производится с помощью как программно сконструированных таблиц, так и аппаратного обеспечения PowerPC. На плоскости (planar) центрального процессора PowerPC находятся 16 сегментных регистров, формирующих действительные адреса и обеспечивающих защиту памяти на уровне сегментов; таким образом, процессная модель памяти на AIX разделена на сегменты. Когда процесс активен, регистры содержат адреса 16-ти сегментов, к которым этот процесс обращается, формируя его адресное пространство.
32-битные приложения на AIX имеют адресное пространство размером 4 Гбайта (2**32=4 Гбайта), и виртуальные адреса в диапазоне от 0x00000000 до 0xFFFFFFFF. AIX разделяет это адресное пространство на 16 независимых сегментов по 256 Мбайт, на каждый из которых ссылается отдельный сегментный регистр. Этот сегмент определяется полубайтами высокого порядка. Например:
- виртуальный адрес 0x0003900C --> сегмент 0;
- виртуальный адрес 0x40000000 --> сегмент 4;
- виртуальный адрес 0x8F98C04F --> сегмент 8;
- виртуальный адрес 0xF6593233 --> сегмент 15.
Для приложения доступен не весь объем этой памяти. Некоторые сегменты зарезервированы операционной системой для ядра AIX, совместно используемых библиотек и исполняемого кода. Другие сегменты зарезервированы для динамической памяти, доступ к которой возможен с помощью malloc(), calloc(), realloc(), free() и подобных им функций. Остальные сегменты доступны процессам shmat() и mmap(), для совместно используемой памяти и проецирования файлов. На рисунке 1 показано использование каждого сегмента в стандартной модели памяти 32-битного процесса. Термин текст (text) обозначает программный код, это понятие широко используется в документации AIX.
Рисунок 1. Использование сегментов в стандартном 32-битном процессе
Резюме по использованию сегментов:
- Первый сегмент, 0x0, зарезервирован системой для текста и данных ядра, поэтому доступ к сегменту 0x0 из процесса пользователя запрещен.
- Сегмент 0x1 содержит текст (код) процесса пользователя. Если одну и ту же исполняемую программу запускают несколько процессов, все эти процессы имеют доступ к одному и тому же сегменту. В системе существует всего один экземпляр текстовой страницы для одного исполняемого файла.
- Сегмент 0x2 содержит данные пользователя, свободную память и стек. Этот сегмент принадлежит процессу пользователя и является одним из самых интересующих аспектов в обсуждении Java-процессов. В следующем разделе он будет рассмотрен более детально.
- Сегмент 0xD содержит текст (код) совместно используемых библиотек. Каждый модуль совместно используемой библиотеки отображен в этом сегменте, распределенном между всеми 32-битными процессами пользователя.
- Сегмент 0xF содержит данные совместно используемых библиотек процесса, и принадлежит процессу пользователя.
- Сегменты 0x3, 0xC, и 0xE зарезервированы для процесса пользователя, чтобы подключиться к адресному пространству процесса, если процесс для локализации сегментов совместно используемой памяти вызовет стандартные функции
shmat() или mmap(). Если пользователь не определяет адрес, вызывая shmat() или mmap(), то эти сегменты располагаются по порядку, начиная с сегмента 0x3 и далее по возрастанию адресов. Лучше избегать жесткого задания в коде адреса подключаемой памяти, поскольку это ограничивает возможности портирования приложений. Максимально возможный объем для совместно используемой памяти в этой модели насчитывает 11 сегментов, т.е. 2,75 Гбайта.
Сегмент 0x2 данных программы содержит большую часть информации о процессе, включая:
- Данные пользователя:
- включают инициализированные и неинициализированные глобальные и статичные данные, размещенные в конце сегмента с меньшим адресом.
- Свободная память пользователя:
- располагается после данных пользователя в сторону конца сегмента с большим адресом. Свободная память управляется процессом с помощью
malloc(), calloc(), realloc(), free() и подобных им функциям.
- Данные ядра для этого процесса:
- структуры данных ядра, используемые ядром для управления процессом. Доступ к ним можно получить лишь с помощью процесса, используя системные вызовы.
- Стековая память пользователя:
- после данных ядра для процесса располагается стековая память пользователя основного потока в сторону конца сегмента с меньшим адресом. Если речь идет о многопоточном приложении, то эта стековая память представляет собой только стековую память основного потока. Стековая память других потоков, созданных впоследствии, динамически распределяется из свободной памяти.
Модель большого адресного пространства для 32-битного процесса
Так как система размещает данные пользователя, свободную память и стековую память внутри сегмента данных программы (сегмент 0x2), то системой накладывается ограничение на максимальное значение стековой памяти, свободной памяти и статичных данных в виде значения, чуть меньшего 256 Мбайт. Большинству приложений этого достаточно. Тем не менее, в приложениях, к которым применима хотя бы одна из следующих характеристик, при использовании стандартной модели памяти возникают сбои:
- приложения, требующие для данных пользователя (инициализированных или неинициализированных статичных данных) обширной области памяти в разделе данных программы;
- приложения, использующие функции malloc, brk или sbrk для динамического размещения больших объектов данных в секции свободной памяти программы;
- приложения, создающие много потоков и, следовательно, вынужденные использовать malloc для динамического размещения большого количества стековой памяти потоков;
- приложения, имеющие длинную цепь вызовов или требующие много места для стековой памяти.
Для того чтобы удовлетворить требования этих больших программ, AIX поддерживает другую модель пространства адресов под названием большое адресное пространство. Эта модель разрешает процессам выделять меньше сегментов для совместно используемой памяти, и больше - для данных программы. Число сегментов, которые должны быть перемещены из совместно используемой памяти в данные программы, определяется величиной, называемой "maxdata". Есть три способа определить ее значение.
- При создании исполняемого файла компоновщиком AIX можно задать ненулевой аргумент для команды
ld с помощью опции -bmaxdata:0xN0000000, где N - цифра от 1 до 8, указывающая на число сегментов, которое нужно зарезервировать для данных программы. Таким образом, значение maxdata окажется в заголовке объектного файла XCOFF. Использование команды dump -ov для объектного файла покажет значение maxdata выполняемой программы.
- В версии AIX 5.1 и выше имеется команда
/usr/ccs/bin/ldedit - изменить заголовок XCOFF исполняемого файла, который уже создан. Вот, например, команда для изменения файла a.out, разрешающая использовать шесть сегментов для свободной памяти пользователя:
$/usr/ccs/bin/ldedit -bmaxdata:0x60000000 a.out
|
- Во время выполнения программы пользователи могут определять переменную среды
LDR_CNTRL=MAXDATA, для перезадания значения maxdata в XCOFF-заголовке исполняемого файла программы. Эта функция доступна только в версии AIX 4.3.3.10 и выше. Например:
$ LDR_CNTRL=MAXDATA=0x60000000 a.out |
Так как в этом подходе используется переменная среды, то на всех процессах, запущенных в этой оболочке, и их дочерних процессах будет использоваться установленное значение.
На рисунке 2 показано использование сегментов 32-битным процессом со значением MAXDATA 0х30000000. В этой модели большого адресного пространства куча и пользовательские данные переносятся в три свободные сегмента, выделенные для данных программы, или сегменты 3, 4, 5. Данные ядра для процесса и стек пользователя остаются в сегменте 2. AIX резервирует три сегмента по 256 Мбайт для данных пользователя и кучи, начиная с сегмента 3. Инициализированные и неинициализированные данные помещаются в нижние адреса сегмента 3, и куча начинается сразу после окончания статических данных и растет в сторону сегментов с большим номером. Число сегментов, предназначенных для разделяемой памяти процессов, теперь уменьшается до 8 сегментов вместо 11 в стандартной модели адресации 32-битного адресного пространства. Опять же, если только пользователь не укажет явно адрес при вызове shmat() или mmap(), эти сегменты выделяются по порядку, начиная с сегмента 0x6 в сторону увеличения нумерации.
Рисунок 2. 32-битная модель большого адресного пространства
Модель очень большого адресного пространства для 32-битных процессов.
Только что рассмотренная модель большого адресного пространства позволяет выделить больше сегментов для данных программы путем уменьшения числа сегментов для разделяемой памяти. Перед запуском программы пользователи должны сами решить, сколько сегментов отвести данным программы. Как только программа начала выполнение, внести какие-либо изменения невозможно.
AIX 5.1 представляет модель очень большого адресного пространства, которая содержит более гибкий механизм максимального использования доступных сегментов 32-битными программами, как программными данными, так и разделяемой памятью. Модель очень большого адресного пространства позволяет динамически распределять сегменты между данными программы и разделяемой памятью. Значение maxdata указывает только максимальный размер кучи, но не указывает системе резервировать сегменты специально для нее.
В AIX 5.2 модель очень большого адресного пространства получила дальнейшее развитие и позволяет 32-битным приложениям увеличивать размер кучи вплоть до 13 сегментов (3,25 Гбайта)
Обозначим основные особенности модели очень большого адресного пространства.
- Чтобы использовать в программе модель очень большого адресного пространства, вместе с
maxdata надо указать новое свойство - динамическое выделение сегментов (DSA, dynamic segment allocation).
- Когда указаны ненулевые значения для
maxdata и DSA, сегменты динамически выделяются как для кучи, так и для разделяемой памяти. Младшие адреса сегмента 3 все еще заняты статическими данными, которые занимают столько сегментов, сколько требуется. Остальные сегменты данных, тем не менее, не резервируются под кучу, а получаются динамически. Пока сегмент требуется для области данных программы, он может использоваться через вызовы shmat или mmap. Значение maxdata указывает только верхнюю границу числа сегментов, которые могут быть использованы под кучу. Когда процесс пытается разместить данные в новом сегменте, он может это сделать, только если сегмент не используется shmat или mmap. Чтобы освободить сегмент, можно использовать процедуры shmdt или munmap, и тогда сегмент может использоваться для размещения данных программы. Однако после того как сегмент использовался для области данных, он уже не может использоваться ни для каких других целей, даже если уменьшить размер области данных.
- Для сегментов, динамически распределяемых между кучей и распределенной памятью, операционная система использует разные правила выбора адреса, возвращаемого при вызове распределенной памяти (если не указан конкретный адрес). Обычно процедуры
shmat или mmap возвращают адрес в первом из доступных сегментов. Когда используется модель очень большого адресного пространства, эти процедуры вернут адрес в последнем из доступных сегментов. Обращение к конкретному адресу будет успешным, если адрес находится в сегменте, не задействованном под область данных. Это поведение справедливо для всех процессов, которые указывают свойство DSA.
- Те же самые три метода, которые использовались для запуска процесса в модели большого адресного пространства, могут быть использованы для запуска процесса в модели очень большого адресного пространства:
- При компиляции и компоновке исходных кодов можно указать параметр
-bmaxdata:0xN00000000/dsa. На AIX 5.2 N может быть шестнадцатеричным от 0 до D. На AIX 5.1 N может быть от 1 до 8, как в модели большого адресного пространства.
- После компиляции исходных кодов отредактировать заголовок XCOFF полученного исполняемого файла командой
/usr/css/bin/ldedit (доступна на AIX 5.1 и старше). Например:
$ /usr/ccs/bin/ldedit -bmaxdata:0x80000000/dsa a.out
|
- Для AIX 4.3.3-10 и последующих версий переменная окружения
LDR_CNTRL может быть установлена так, чтобы перекрыть значение maxdata при запуске программы. Например:
$ LDR_CNTRL=MAXDATA=0xC0000000@DSA a.out |
- На AIX 5.2 значение
maxdata для модели очень большого адресного пространства может находиться между 0 и 0хD0000000. Вам, наверное, любопытно, как такое возможно. Так как пять из 16 сегментов необходимы под ядро (0х0), код программы (0х1), стек программы (0х2), текст разделяемых библиотек (0хD) и данные разделяемых библиотек (0xF), значение maxdata не должно быть больше 0хВ0000000. Это еще одно фундаментальное различие между моделями большого и очень большого адресного пространства. Когда используется модель очень большого адресного пространства и maxdata устанавливается в значение большее или равное 0хВ0000000, операционная система освобождает сегменты текста и данных разделяемых библиотек. Все необходимые процессу тексты и данные разделяемых библиотек будут загружены в сегмент 0х2 как частные разделяемые объекты (private shared objects). Даже если другой процесс уже загрузил разделяемые объекты в системную память, этот процесс все равно загрузит другую копию разделяемых объектов в собственный сегмент данных. Этим изменением могут быть затронуты производительность процесса и системы. Однако это изменение позволяет процессу увеличить собственную кучу вплоть до 13 сегментов (3,25 Гбайта).
Кроме включения сегментов 0хD и 0хF в динамически выделяемую область, остальной механизм выделения памяти остается таким же, как при значении maxdata, меньшим 0хВ0000000. Сегмент кучи начинается после инициализированных и неинициализированных данных и возрастает в сторону сегментов с большими номерами, не пропуская сегмента 0xD.
- Некоторым большим приложениям не нужна очень большая куча, но нужна очень большая разделяемая память. Обычная 32-битная адресация позволяет процессу занимать до 11 сегментов (2,75 Гбайта) для разделяемой памяти. На AIX 5.2 и следующих версиях модель очень большого адресного пространства позволяет иметь до 13 сегментов (3,25 Гбайта) разделяемой памяти, применяя уже упомянутый механизм - путем освобождения сегментов текстов и данных распределенных библиотек. Когда значение
maxdata равно 0 и указано свойство DSA, сегменты 0х3 - 0хЕ резервируются только под разделяемую память. Все необходимые процессу разделяемые тексты и данные загружаются в сегмент 0х2. Для кучи, включая не главные потоки, также выделяется место в сегменте 0х2, так как сегмент 0х3 зарезервирован только под разделяемую память.
Если программа использует эту модель (maxdata = 0 и присутствует DSA), все 13 сегментов (3,25 Гбайта) отводятся под разделяемую память, начиная с сегмента 0хЕ по сегмент 0х3. Сегмент 0х2 будет использован под стек, кучу и все используемые процессом распределенные объекты.
Эта модель подходит таким приложениям как менеджеры баз данных, чьи запросы к собственной памяти выполняются на сегментах распределенной памяти. Тем не менее, приложение само должно следить, чтобы не произошло переполнения стека, что может привести к повреждению сегмента 0х2.
Следующие рисунки помогут лучше понять очень большое адресное пространство. На рисунке 3 процесс запущен с maxdata = 0хА0000000 и установленным параметром DSA. Куча процесса может возрасти до 10 сегментов (2,5 Гбайта). Заметим, что система не резервирует 10 сегментов (0х3 - 0хС) для кучи. Сегмент 0х3 всегда занят данными программы, потому что в нем находятся как инициализированные, так и не инициализированные данные. Остальные 10 сегментов (0х4 - 0хС) доступны для динамического распределения между кучей программы и распределенной памятью, так что потенциально программа может занять под кучу все 10 сегментов (0х3 - 0хС). Так как значение maxdata установлено в 0хА0000000, то, устанавливая верхнюю границу кучи в сегмент 0хС, система резервирует сегмент 0хЕ только для распределяемой памяти, которая потенциально также может занять 10 сегментов (0х4 - 0хС, 0хЕ) и вырасти до 2,5 Гбайта.
Модель очень большого адресного пространства применяется при запуске программ с maxdata меньшим, чем 0хВ0000000. Как уже упоминалось, AIX 5.1 поддерживает эту модель только в том случае, если maxdata меньше или равна 0х80000000.
Рисунок 3. 32-битная модель очень большого адресного пространства, MAXDATA = 0xA0000000/DSA
На рисунке 4 процесс запущен с maxdata = 0хВ0000000 и указанным параметром DSA. Куча процесса может занимать до 11 сегментов (2,75 Гбайта). Обратите внимание, что тексты и данные распределенных библиотек больше не занимают сегменты 0хD и 0хF, а переместились в сегмент 0х2. Еще раз повторим - младшие адреса сегмента 0х3 заняты инициализированными и неинициализированными данными, далее идет куча приложения. Другие 10 сегментов (0х4 - 0хD) не зарезервированы ни под кучу, ни под распределенную память, но могут использоваться обеими. Однако сегменты 0хЕ и 0хF зарезервированы специально под распределенную память. В этом примере, когда maxdata установлено в 0xB0000000/dsa, куча может занять 11 сегментов (0х3 - 0хD), а распределенная память - 12 (0х4 - 0хF) и вырасти до 3 Гбайт. В этой модели пользователи могут увеличивать maxdata до значения 0хD0000000, и в этом случае куча может занять 13 сегментов (3,25 Гбайта).
AIX 5.1 также не поддерживает эту модель очень большого адресного пространства.
Рисунок 4. 32-битная модель очень большого адресного пространства, MAXDATA = 0xB0000000/DSA
На рисунке 5 процесс запущен с maxdata = 0 и указанным значением DSA. В этом случае не только разделенные библиотеки и текст, но также данные программы и куча перемещаются в сегмент 2. Все 13 сегментов (0х3 - 0xF) специально резервируются под разделяемую память процесса, которая занимает непрерывное пространство в 3,25 Гбайта.
Эта модель очень большого адресного пространства также не поддерживается в AIX 5.1.
Рисунок 5. 32-битная модель очень большого адресного пространства, MAXDATA=0/DSA
Параметры -Xms и -Xmx (для JDK 1.1.8 -ms и -mx)
Теперь, когда были рассмотрены различные модели памяти на AIX, можно объяснить, как они применяются в Java на AIX. Любой программист Java-приложений знает, как использовать параметры -Xms и -Xmx командной строки (на JDK 1.1.8 -ms и -mx), чтобы запросить начальный и максимальный размеры кучи Java. Однако параметры -Xms и -Xmx могут работать только в области, разрешенной адресной моделью JVM. Чтобы получить в Java кучу необходимого размера, необходимо указать его через параметры -Xms и -Xmx, и в случае необходимости установить maxdata.
Чтобы понять, насколько большой необходимо сделать кучу Java, необходимо отличать кучу Java от программной кучи, о которой говорилось при рассмотрении 32-битных моделей адресного пространства AIX. Чтобы различать программную кучу и кучу Java, назовем программную кучу платформенно-зависимой кучей.
Когда JVM запускается с использованием команды Java или команды запуска JVM (далее в статье называемыми командами Java), команда запускается как процесс AIX. Этот процесс, скорее всего, представляет собой программу, написанную на каком-либо из языков типа С или С++. Как и у любого другого процесса AIX, у него есть стек, статичные данные и, возможно, разделяемая память для межпроцессных взаимодействий (IPC) или размещения файлов. Кроме того, для различных внутренних вычислений JVM тоже требуется место в платформенно-зависимой куче, например буферы, используемые JIT, структуры данных для сборщика мусора и разнообразные связующие не Java-программы, вызываемые JVM. Если Java-приложение использует JNI для вызова кода, написанного на других языках, то динамическое выделение памяти в этом коде тоже будет происходить в платформенно-зависимой куче. Стеки всех потоков, кроме главных, динамически выделяются в платформенно-зависимой куче при создании потоков. Если платформенно-зависимая куча полностью заполняется во время выполнения программы, JVM может выдать сообщение об ошибке, говорящее, что больше нельзя создать ни одного потока.
С другой стороны, куча Java полностью управляется JVM. Когда JVM выполняет Java-программу, то любой создаваемый Java-объект будет размещен в куче Java. Можно представить себе кучу Java как большой буферный пул, управляемый JVM. Если куча Java заполнится во время выполнения, JVM создаст исключительную ситуацию java.lang.OutOfMemory. Поскольку JVM выполняется внутри процесса AIX, то управляемая ею куча Java должна находиться или в платформенно-зависимой куче или в области разделяемой памяти. На самом деле, подходы, использованные в различных реализациях Java на AIX, менялись вместе с появлением на AIX новых возможностей для масштабируемости.
Как разные версии Java на AIX организуют кучу Java
В таблице 1 показаны свойства, связанные с организацией кучи Java для различных реализаций Java на AIX. Модель памяти Java 1.4.1 обсуждается отдельно, так как она поддерживает модель очень большого адресного пространства, которая гораздо универсальнее, чем модель большого адресного пространства.
Таблица 1
|
|
1.1.8
|
1.2.2
|
1.3.1
|
1.4.0
|
Значение maxdata по умолчанию
| 0x00000000 | 0x50000000 | 0x80000000 | 0x80000000 | |
Реализация кучи Java
| mmap( ) | mmap( ) | Xmx<1 Гбайт : malloc( ), иначе: mmap( )1 | Xmx<1 Гбайт : malloc( ), иначе: mmap( )1 | |
Значение -Xms по умолчанию
| 1 МБайт | 1 МБайт | 1 МБайт | 4 МБайта | |
Значение -Xmx по умолчанию
| 32 МБайта | 64 МБайта | 64 МБайта | 64 МБайта | |
Значение -Xmx максимальное2
| 2 Гбайта - 1 | 1280 Мбайт | 1 Гбайт | 1 Гбайт | |
Максимально возможная куча Java
| 8 сегментов ( 2 Гбайта - 1 ) | 8 сегментов ( 2 Гбайта - 1 ) | 10 сегментов (2,5 Гбайта) | 10 сегментов (2,5 Гбайта) | |
Максимально возможная платформенно-зависимая куча
3
| < 2,5 Гбайта - куча Java | < 2,5 Гбайта - куча Java | Maxdata=0: <<256 Мбайт, Maxdata >0 : < 2,5 Гбайта - куча Java | Maxdata=0: <<256 Мбайт, Maxdata >0 : < 2,5 Гбайта - куча Java |
1
Начиная с JDK 1.3.1, куча Java создается функцией malloc(), когда запрашиваемый размер меньше 1 Гбайта, в противном случае используется функция mmap(). Однако можно заставить Java пользоваться функцией mmap() независимо от размера кучи, установив переменную окружения IBM_JAVA_MMAP_JAVA_HEAP=true.
2 Если в командной строке Java не указать maxdata, то максимальное значение, которое можно указать через -Xms и -Xmx, зависит от сочетания значения maxdata и реализации кучи Java. Эти значения объясняются ниже.
- JDK 1.1.8
- По умолчанию куча создается функцией
mmap(). Поскольку maxdata равно 0х0000000, все сегменты между 0х3 и 0хС доступны для mmap(). Однако из-за ошибки в коде, который переводит параметры командной строки в знаковые 32-битные целые, придется остановиться на значении 0x7FFFFFF байт. Максимальное значение, которое можно указать в командной строке, равно 214748647 (или 2 Гбайта - 1). Значение в следующем ряду то же самое, без необходимости менять значение maxdata. Более подробно это обсуждается в статье How to increase memory in AIX for Java applications.
- JDK 1.2.2
- По умолчанию куча создается, как и в 1.1.8, функцией
mmap(). Значение maxdata равно 0x50000000, и JVM требует, чтобы куча Java представляла собой непрерывное адресное пространство, так что по умолчанию JVM может получить через mmap() только пять непрерывных сегментов (0х8 - 0хС). Если запустите JVM без настроек, то можно указать размер кучи Java не более чем 1280 Мбайт. Чтобы получить максимально возможное значение, указанное в следующем ряду, необходимо изменить maxdata до значения 0х20000000 или меньше.
- JDK 1.3.1, 1.4.0
- Значение
maxdata равно 0х80000000, так что mmap() остаются только два непрерывных сегмента (0xB - 0xC), или 512 Мбайт. Ирония состоит в том, что JVM начинает использовать mmap() для создания кучи Java, как только запрашиваемое в командной строке значение становится больше 1 Гбайта. Любой вызов Java с запросом размера кучи больше 1 Гбайта не сработает без изменения значения maxdata, поэтому в этой строчке указан 1 Гбайт. Максимальное значение на следующей строчке - 2,5 Гбайта, что больше, чем в предыдущих реализациях JDK. Чтобы получить кучу размером 2,5 Гбайта, нужно сделать две вещи: указать -Xmx 2560m и установить maxdata в 0х00000000, чтобы освободить все 10 непрерывных сегментов (0x3 - 0xC) для mmap().
3 При попытке получить максимально возможную кучу Java стоит оценить, каких размеров должна быть платформенно-зависимая куча для JVM и приложения. Как объяснялось ранее, для работы JVM требуется много памяти - для буферов JIT, стеков потоков и прочих динамических вызовов JNI. Объяснение значений в этом ряду приведено ниже.
- JDK 1.1.8 и 1.2.2
- Поскольку куча Java создается
mmap(), 10 сегментов (0х3 - 0хС), или 2,5 Гбайта, делятся между кучей Java и платформенно-зависимой кучей, поэтому максимальное значение платформенно-зависимой кучи указано как "< 2.5 GB - куча Java". Знак < указывает, что инициализированным и неинициализированным данным тоже нужна память, которая берется в сегменте данных программы.
- JDK 1.3.1 и 1.4.0
- Максимальный размер собственной кучи зависит от величины
maxdata. Если maxdata > 0, то этот размер считается так же, как в JDK 1.1.8 и 1.2.2. Если maxdata = 0, то стек, статичные данные и собственная куча размещаются в сегменте 2. Поэтому размер будет << (много меньше), чем 256 Мбайт.
Реализация кучи Java в JDK 1.4.1 на AIX
Как упоминалось, JDK 1.4.1 поддерживает гораздо более универсальную модель очень большого адресного пространства. Когда включается поддержка этой модели - через установление maxdata в значение 0xN0000000/dsa - доступные сегменты динамически распределяются между данными программы и разделяемой памятью, а значение 0xN0000000 указывает только на максимальное значение памяти, доступной данным программы. Ниже показываются величины кучи Java и платформенно-зависимой кучи для Java-процессов, запущенных с тремя типами моделей очень большого адресного пространства.
maxdata равно 0x00000000/dsa:
- платформенно-зависимая куча в сегменте 2 делит 256 Мбайт со стеками потоков, памятью JVM, кроме объектов, загруженным кодом и данными разделяемых библиотек;
- сегменты с 3 по 15 зарезервированы специально для кучи Java (созданной через функции
shmat/mmap). Куча Java может занимать до 13 сегментов (3,25 Гбайта). Если для нее запрашивается меньше 13 сегментов, то остальное будет доступно разделяемой памяти;
- сегменты, выделяемые с помощью
shmat/mmap, начинаются с сегмента 15 и растут в сторону сегментов с меньшими номерами.
maxdata не меньше, чем 0xB0000000/dsa:
- платформенно-зависимая куча в сегменте 3;
- сегменты с 4 по 15 доступны для динамического распределения между кучей Java и платформенно-зависимой кучей (для выделения памяти используется
shmat/mmap). Куча Java может занимать до 12 сегментов (3 Гбайта). Если запрашивается меньше 12 сегментов, то остальное будет доступно для платформенно-зависимой кучи и распределенной памяти;
- сегменты, выделяемые с помощью
shmat/mmap, начинаются с сегмента 15 и растут в сторону сегментов с меньшими номерами.
maxdata меньше, чем 0хА0000000/dsa:
- платформенно-зависимая куча начинается с сегмента 3;
- сегменты с 4 по 12 доступны для кучи Java (сегмент 14 исключается, так как куча Java должна быть непрерывной). Куча Java может занимать до 9 сегментов (2,25 Гбайта);
- сегменты, выделяемые с помощью
shmat/mmap, начинаются с сегмента 12 и растут в сторону сегментов с меньшими номерами.
При использовании JDK 1.4.1 больше не нужно указывать переменную окружения LDR_CNTRL=MAXDATA. JVM 1.4.1 сама устанавливает подходящее значение maxdata исходя из запрошенного через параметр -Xmx максимального размера кучи Java. Если перед запуском JVM указать LDR_CNTRL=MAXDATA, то JVM будет использовать указанное значение. В противном случае будет применен следующий алгоритм установки LDR_CNTRL=MAXDATA:
- На AIX 5.2 и следующих версиях:
-
если куча 3 Гбайта или больше,
LDR_CNTRL=MAXDATA=0@DSA;
если куча между 2,3 Гбайта и 3 Гбайта, LDR_CNTRL=MAXDATA=0хВ0000000@DSA;
если куча меньше 2,3 Гбайта, LDR_CNTRL=MAXDATA=0хА0000000@DSA.
- На AIX 5.1
-
Хотя модель очень большого адресного пространства доступна на AIX 5.1, реализация имеет существенные отличия и поэтому JDK 1.4.1 не поддерживает использование модели очень большого адресного пространства на AIX 5.1. JVM все еще устанавливает
LDR_CNTRL=MAXDATA исходя из максимально возможного размера кучи, но не использует модель очень большого адресного пространства.
Если размер кучи больше 1 Гбайта, LDR_CNTRL=MAXDATA устанавливается в подходящее значение. Стоит обратить внимание на обратную связь при таком подходе, так как чем больше размер кучи, тем меньше сегментов выделяется через значение LDR_CNTRL=MAXDATA. Например, для кучи в 1 Гбайт LDR_CNTRL=MAXDATA устанавливается в 0х60000000, а для кучи в 1,5 Гбайта LDR_CNTRL=MAXDATA равно 0х40000000.
Если размер кучи меньше 1 Гбайта, LDR_CNTRL=MAXDATA устанавливается в 0х80000000.
 |
64-битная модель памяти JVM на AIX
К счастью, в 64-битном мире все гораздо проще. Больше не нужно жонглировать ограниченным набором сегментов. Если даже очень большая модель адресного пространства 32-битной JVM не сможет удовлетворить потребности для кучи Java-приложения или платформенно-зависимой кучи, то стоит рассмотреть возможность перехода на 64-битную версию Java. В этой части статьи кратко рассматривается 64-битное адресное пространство на AIX и рассказывается, насколько проще становится модель памяти.
Адресное пространство 64-битных процессов на AIX
В модели 64-битных процессов используется та же концепция сегментов, что и в 32-битных процессах. Размер сегмента все также 256 Мбайт, но число доступных сегментов теперь 232, а не 24. Следовательно, 64-битный процесс может использовать до 1 Эбайта (экзабайта) памяти, что может быть вычислено так:
232 сегментов x 256 [Мбайт/сегмент] = 232x 228 байт = 260 = 1 Эбайт
|
Если вам интересно, что такое экзабайт, обратитесь к таблице 2, где приведены определения, обычно используемые в компьютерной индустрии.
Таблица 2
|
Префикс
|
Символ
|
Степень 10
|
Степень 2
|
Количество байт
| | Кило- | к илиK**
| 103
| 210
| 1,024 | | Мега- | M | 106
| 220
| 1,048,576 | | Гига- | Г | 109
| 230
| 1,073,741,824 | | Тера- | T | 1012
| 240
| 1,099,511,627,776 | | Пета- | П | 1015
| 250
| 1,125,899,906,842,624 | | Экза- | Э | 1018 *
| 260
| 1,152,921,504,606,846,976 |
Чтобы обращаться к такому огромному пространству в модели 64-битных процессов указатель занимает 64 бита. На рисунке 6 показано, как сегменты используются в этом огромном адресном пространстве.
Рисунок 6. 64-битная модель адресного пространства
Первые 16 сегментов (0-4 Гбайт) выведены из общего использования, чтобы обеспечить совместимость с 32-битными процессами.
Сегменты с 16 по 7*167 (4 Гбайт - 228 Пбайт) используются для пользовательских текста и данных, а также для кучи. Код пользовательских программ отображается в первый сегмент этой области. Кроме того, пользовательские данные отображаются в следующий сегмент этой области. В обоих случаях, если сегмента не хватает, к адресному пространству процесса будут добавлены еще сегменты.
Следующие 167 сегментов (448-512 Пбайт) предназначены для вызовов функций shmat() и mmap(), чтобы выделить 64-битному процессу разделяемую память.
Следующие 167 сегментов (512-576 Пбайт) предназначены для загруженных в адресное пространство объектов, например с помощью системных вызовов dlopen() или load(), или для разделяемых объектных файлов, запрещающих остальным чтение и выполнение. Иногда это делается с помощью связующих программ других производителей, причем это остается невидимым для пользователя (дополнительную информацию можно найти в статье Developing and porting C and C++ applications on AIX).
Следующие 167 сегментов (576-640 Пбайт) используются для загрузки кода и данных 64-битных разделяемых библиотек, которые будут использоваться всеми 64-битными процессами системы. В этой же области будут созданы и данные разделяемых библиотек, предназначенных для личного использования процессом. В обоих случаях, если в сегменте достаточно места для кода или данных распределенных библиотек, этот сегмент будет использован. Иначе к адресному пространству процесса будет добавлен еще один сегмент.
Сегменты с 10*167 по 15*167 (640-960 Пбайт) зарезервированы для системы и закрыты для пользовательских процессов.
Последние 167 сегментов (960 Пбайт - 1 Эбайт) используются для стеков 64-битных процессов. Стек возрастает с последнего адреса, 0x0FFF_FFFF_FFFF_FFFF, в сторону первого адреса в области, чтобы была возможность использовать более одного сегмента.
Параметры -Xms и -Xmx для 64-битной Java
Очевидно, что с 64-битными версиями Java больше не надо сильно беспокоиться о том, что память может закончиться. Куча Java может быть очень большой. Однако, чтобы воспользоваться 64-битной Java, необходимо, чтобы весь код связующих программ или код, использующийся через JNI, был скомпилирован как 64-битные программы. Еще одна причина для беспокойства - большая пауза, возникающая во время работы сборщика мусора на такой огромной куче.
Заключение
Данного подробного описания адресных пространств 32- и 64-битных процессов вполне хватит, чтобы разобраться с большинством проблем, возникающих из-за неправильных настроек размера кучи в Java.
Обязательно прочтите третью из этой серии статей, в которой будет обсуждение сборщика мусора и реализации потоков в Java. Эта информация поможет получить необходимую масштабируемость и производительность.
Ресурсы
Об авторе  | |  | Ли Ченг (Lee Cheng) работает старшим консультантом для поставщиков pSeries и программного обеспечения для AIX. Она предоставляет им поддержку для сертификации программного обеспечения, портирования и локализации приложений. Прежде чем присоединиться к группе RS/6000 ISV Technical Support, она работала разработчиком компиляторов и компонентов управления системой AIX. Ли обладает степенью магистра в области компьютерных наук, полученной в Университете Кентукки (University of Kentucky). Связаться с Ли можно по адресу chenglc@us.ibm.com. |
Выскажите мнение об этой странице
|  |