Как Java (JVM) выделяет стек для каждого потока

Приложение Java запускается с одной кучей для всех потоков. Каждый поток имеет свой собственный стек.

Когда приложение Java запущено, мы используем опцию JVM -Xms и -Xmx для управления размером кучи и -Xss для управления размером стека.

Я понимаю, что создаваемая куча становится "управляемой" памятью JVM, и все создаваемые объекты помещаются туда.

Но как работает создание стека? Создает ли Java стек для каждого потока при его создании? Если да, то где именно стек находится в памяти? Это, конечно, не в "управляемой" куче.

Создает ли JVM стек из собственной памяти или предварительно выделяет раздел управляемой области памяти для стека? Если да, то как JVM знает, как могут быть созданы потоки?

Ответы

Ответ 1

Есть несколько вещей о стеках потоков, о которых говорит спецификация Java. Среди прочего:

  • Каждый поток виртуальной машины Java имеет собственный стек виртуальной машины Java, созданный одновременно с потоком.

  • Поскольку стек виртуальной машины Java никогда не управляется напрямую, кроме как для push и pop frames, фреймы могут быть выделены в кучу. Память для стека виртуальной машины Java не обязательно должна быть смежной.

  • Спецификация позволяет стекам виртуальной машины Java либо иметь фиксированный размер, либо динамически расширять и сжимать, как требуется при вычислении.

Теперь, если мы сосредоточимся на реализациях JVM, таких как HotSpot, мы можем получить дополнительную информацию. Вот несколько фактов, которые я собрал из разных источников:

  • Минимальный размер стека в HotSpot для потока, по-видимому, исправлен. Для этого используется вышеупомянутая опция -Xss. (Источник)

В Java SE 6 значение по умолчанию для Sparc равно 512k в 32-разрядной виртуальной машине и 1024k в 64-разрядной виртуальной машине.... Вы можете уменьшить размер стека, выполнив опцию -Xss.... 64k - наименьшее количество пространства стека, разрешенного для потока.

  • JRockit выделяет память отдельно от кучи, где расположены стеки. (Источник)

Обратите внимание, что JVM использует больше памяти, чем просто кучу. Например, методы Java, стеки потоков и собственные дескрипторы выделяются в памяти отдельно от кучи, а также внутренние структуры данных JVM.

  • Прямое сопоставление между потоком Java и собственным потоком ОС в HotSpot. (Источник).

  • Но стек потоков Java в HotSpot управляется программным обеспечением, это не собственный поток стека ОС. (Источник)

Он использует отдельный стек программного обеспечения для передачи аргументов Java, тогда как собственный C-стек используется самой VM. Ряд внутренних переменных JVM, таких как счетчик программ или указатель стека для потока Java, хранятся в переменных C, которые не гарантируются всегда в аппаратных регистрах. Управление этими структурами интерпретатора программного обеспечения потребляет значительную долю общего времени выполнения.

  • JVM также использует тот же стек потоков Java для собственных методов и вызовов времени выполнения JVM (например, загрузка классов). (Источник).

  • Интересно, что даже выделенные объекты иногда могут быть помещены в стек вместо кучи в качестве оптимизации производительности. (Источник)

JVM могут использовать метод, называемый escape-анализом, посредством которого они могут сказать, что некоторые объекты остаются ограниченными одним потоком на протяжении всего их жизненного цикла, а это время жизни ограничено временем жизни данного кадра стека. Такие объекты можно безопасно выделить в стеке вместо кучи.

И поскольку изображение стоит тысячи слов, вот один из Джеймс Блум Java memory


Теперь, отвечая на некоторые из ваших вопросов:

Как JVM знает, как могут быть созданы потоки?

Это не так. Легко доказывается противоречием, создавая переменное количество потоков. Он делает некоторые предположения о максимальном количестве потоков и размере стека для каждого потока. Вот почему у вас может быть нехватка памяти (а не память кучи!), Если вы выделяете слишком много потоков.

Создает ли Java стек для каждого потока при его создании?

Как упоминалось ранее, каждый поток виртуальной машины Java имеет собственный стек виртуальной машины Java, созданный одновременно с потоком. (Источник).

Если да, то где именно стек находится в памяти? Это, конечно, не в "управляемой" куче.

Как указано выше, спецификация Java позволяет хранить стек памяти на куче, технически говоря. Но по крайней мере JRockit JVM использует другую часть памяти.

Создает ли JVM стек из собственной памяти или предварительно выделяет раздел управляемой области памяти для стека?

Стек управляется JVM, потому что спецификация Java предписывает, как она должна себя вести: стек виртуальной машины Java хранит фреймы (§2.6). Стек виртуальной машины Java аналогичен стеку обычного языка. Одним из исключений являются стеки Native Method, используемые для методов native. Подробнее об этом еще раз в спецификация.

Ответ 2

JVM использует больше памяти, чем просто кучу. Например, методы Java, стеки потоков и собственные ручки выделяются в памяти отдельно от кучи, а также внутренние структуры данных JVM.

Дальнейшее чтение.

Итак, чтобы ответить на ваши вопросы:

Создает ли Java стек для каждого потока при его создании?

Да.

Если да, то где именно стек находится в памяти?

В выделенной памяти JVM, но не в куче.

Если да, то как JVM знает, как могут быть созданы потоки?

Это не так.

Вы можете создать столько, сколько хотите, пока не превысите свою JVM-память и не получите

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

EDIT:

Все вышеперечисленное относится к Jrockit JVM, хотя мне трудно поверить, что другие JVM будут отличаться от таких фундаментальных проблем.