Являясь объектно-ориентированным, кроссплатформенным языком, Java всегда была сложной точкой знаний об объектах, памяти и т. д. Поэтому даже новичок в Java должен иметь более или менее некоторые знания о JVM. Можно сказать, что соответствующие знания о JVM — это, по сути, точка знаний, которую должен изучить каждый разработчик Java, а также точка знаний, которую необходимо проверить во время собеседований.
В структуре памяти JVM есть две общие области: память кучи и память стека (если не указано иное, стек, упомянутый в этой статье, относится к стеку виртуальной машины).Относительно разницы между кучей и стеком многие разработчики также многие книги или статьи в Интернете, вероятно, начинаются так:
1. Куча — это область памяти, совместно используемая потоками, а стек — область памяти, совместно используемая исключительно потоками.
2. В куче в основном хранятся экземпляры объектов, а в стеке в основном хранятся ссылки на различные базовые типы данных и объекты.
Впрочем, автор может сказать всем ответственно,Ни один из двух приведенных выше выводов не является полностью правильным.
В этой статье вы сначала поймете, почему я говорю: «куча — это область памяти, разделяемая потоками, а стек — это область памяти, разделяемая исключительно потоками». ? О соответствующих знаниях о структуре памяти JVM вы можете прочитатьСтруктура памяти JVM VS Модель памяти Java VS Объектная модель Java,Неожиданно, вопросы о структуре памяти JVM могут быть такими сложными?и другие статьи.
Прежде чем я перейду к делу, позвольте мне задать вопрос, который, кажется, не имеет ничего общего с этим вопросом:Как процесс выделения памяти для объектов Java обеспечивает безопасность потоков?
Как процесс выделения памяти для объектов Java обеспечивает безопасность потоков?
Мы знаем, что Java является объектно-ориентированным языком, и все объекты, которые мы используем в Java, должны быть созданы.В Java существует множество способов создания объекта, но в любом случае в процессе создания объекта необходимо для создания.Выполнить выделение памяти.
В процессе выделения памяти объекта ссылка объекта в основном указывает на эту область памяти, а затем выполняется операция инициализации.
Однако, поскольку куча совместно используется во всем мире, несколько потоков могут одновременно запрашивать место в куче.Затем, в параллельном сценарии, что, если два потока последовательно указывают объектные ссылки на одну и ту же область памяти.
Чтобы решить эту проблему параллелизма, процесс выделения памяти для объектов должен управляться синхронно. Но мы все знаем,Какая бы схема синхронизации не использовалась (на самом деле виртуальная машина может использовать CAS), она повлияет на эффективность выделения памяти.
Выделение объектов Java — это высокочастотная операция в Java, поэтому люди думают о другом способе повышения эффективности. Здесь мы сосредоточимся на решении виртуальной машины HotSpot:
Каждый поток предварительно выделяет небольшой кусок памяти в куче Java, а при выделении памяти объекту выделяет ее прямо в своей "частной" памяти. Когда эта часть области израсходуется, он выделяет новый " личная" память. ОЗУ.
Эта схема называется распределением TLAB, а именно Thread Local Allocation Buffer. Эта часть буфера отделена от кучи, но является эксклюзивной для локального потока.
Что такое ТЛАБ
TLAB — это выделенное пространство, разделенное виртуальной машиной в райской памяти кучи, которая является эксклюзивной для потока. Когда функция TLAB виртуальной машины запускается, когда поток инициализируется, виртуальная машина выделяет пространство TLAB для каждого потока, которое используется только текущим потоком, так что у каждого потока есть отдельное пространство. быть выделенным, он находится в разделе «Выделить на своем собственном пространстве», чтобы не было конкуренции, что может значительно повысить эффективность распределения.
Вы обратили внимание на описания «независимо от потока», «используется только текущим потоком» и «отдельно принадлежит каждому потоку» в приведенном выше описании?
Поэтому из-за технологии TLAB память кучи не полностью разделяется потоками, а часть пространства в eden-области выделяется для монопольного использования потоками.
Здесь стоит отметить, что мы говорим, что TLAB является эксклюзивным для потоков, но он является эксклюзивным только для потоков в действии «распределения» и совместно используется потоками в таких действиях, как чтение и сборка мусора. И нет никакой разницы в использовании.
То есть, хотя каждый поток будет претендовать на TLAB в памяти кучи при инициализации, это не означает, что память в этой области TLAB полностью недоступна для других потоков, чтение других потоков все еще возможно, но не может. использоваться в этой области.Просто выделите память в этой области.
Более того, после выделения TLAB это никак не влияет на перемещение и переработку объекта.То есть, хотя объект может выделять память через TLAB в начале и хранить ее в области Eden, это все равно будет мусором собраны или перемещены в Survivor Space, Old Gen Wait.
Еще один момент, который следует отметить, это то, что мы говорим, что TLAB размещен в области eden, потому что сама область eden не слишком велика, и память пространства TLAB также очень мала, по умолчанию она занимает только 1% всего Eden. космос. Следовательно, должны быть какие-то большие объекты, которые нельзя выделить напрямую в TLAB.
При столкновении с большим объектом, который не может быть размещен в TLAB, объект все еще может быть размещен в области eden или old age и т. д., но это размещение требует управления синхронизацией, поэтому мы часто говорим:Маленькие объекты размещаются более эффективно, чем более крупные объекты.
Проблемы с TLAB
Хотя TLAB в определенной степени значительно повышает скорость выделения объектов, с TLAB не обошлось без проблем.
Как мы уже говорили ранее, поскольку область памяти TLAB не очень велика, часто могут возникать ситуации нехватки. В «Практической виртуальной машине Java» есть такой пример:
Например, пространство TLAB потока имеет 100 КБ, из которых 80 КБ были использованы. Когда необходимо выделить еще один объект размером 30 КБ, его нельзя выделить непосредственно в TLAB. В этом случае есть два решения:
1. Если пространство, необходимое для объекта, превышает оставшееся пространство в TLAB, объект выделяется непосредственно в куче памяти.
2. Если пространство, необходимое для объекта, превышает оставшееся пространство в TLAB, текущий TLAB отбрасывается, а пространство TLAB повторно применяется для выделения памяти снова.
Вышеуказанные две схемы имеют свои преимущества и недостатки.Если будет принята схема 1, может возникнуть экстремальная ситуация, то есть в TLAB останется только 1 КБ, что приведет к тому, что большинство объектов, которые необходимо разместить впоследствии, размещаться непосредственно в куче памяти.
Если будет принята схема 2, также возможно, что TLAB часто отбрасываются, а TLAB часто применяются.Мы знаем, что, хотя выделение памяти на TLAB является эксклюзивным для потоков, сам процесс разделения памяти TLAB от кучи может действительно конфликтовать. ., поэтому процесс выделения TLAB фактически требует управления параллелизмом. И частое выделение TLAB теряет смысл использования TLAB.
Чтобы решить проблемы этих двух схем, виртуальная машина определяет значение refill_waste, что можно перевести как «максимальное неиспользуемое пространство».
Когда запрошенная выделенная память больше, чем refill_waste, будет выбрано выделение памяти в куче. Если оно меньше значения refill_waste, текущий TLAB будет отброшен, а TLAB будет воссоздан для выделения памяти объекта.
В предыдущем примере общее пространство TLAB составляет 100 КБ, используется 80 КБ и остается 20 КБ.Если значение refill_waste установлено на 25 КБ, то если память нового объекта больше 25 КБ, память кучи будет выделяется напрямую. Если он меньше 25 КБ, предыдущий будет отброшен. Для этого TLAB перераспределите пространство TLAB и выделите память для нового объекта.
Связанные параметры, используемые TLAB
Функция TLAB может быть включена или отключена.Вы можете указать, следует ли включить выделение TLAB, установив параметр -XX:+/-UseTLAB.
По умолчанию TLAB составляет 1% от площади eden.Вы можете установить процентный размер пространства Eden, занимаемого пространством TLAB, с помощью параметра -XX:TLABWasteTargetPercent.
По умолчанию пространство TLAB будет постоянно корректироваться во время выполнения, чтобы система могла достичь наилучшего рабочего состояния. Если вам нужно отключить автоматическое изменение размера TLAB, вы можете использовать -XX:-ResizeTLAB для отключения и -XX:TLABSize, чтобы вручную указать размер TLAB.
Refill_waste TLAB также можно настроить.Значение по умолчанию – 64, что означает, что около 1/64 размера пространства используется как refill_waste.Используйте параметр: -XX:TLABRefillWasteFraction для настройки.
Если вы хотите наблюдать за использованием TLAB, вы можете использовать параметр -XX+PringTLAB для трассировки.
Суммировать
Чтобы обеспечить потокобезопасность при выделении объектной памяти, виртуальная машина HotSpot предоставляет технологию под названием TLAB (Thread Local Allocation Buffer).
Когда поток инициализируется, виртуальная машина выделяет кусок пространства TLAB для каждого потока, который используется только текущим потоком.Когда память должна быть выделена, она выделяется в своем собственном пространстве, чтобы не было конкуренции, и эффективность распределения может быть значительно улучшена.
Поэтому предложение «куча — это область памяти, разделяемая потоками» не совсем корректна, потому что TLAB — это часть памяти кучи, и она действительно разделяется потоками при чтении, но при выделении памяти разделяется исключительно потоками.
Пространство TLAB на самом деле невелико, поэтому большие объекты, возможно, все же придется размещать непосредственно в куче памяти. Затем шаг выделения памяти объекта состоит в том, чтобы сначала попытаться выделить TLAB.После того, как места недостаточно, затем решить, должен ли он непосредственно войти в старость, а затем определить, следует ли снова выделить его через eden или в старости.
скажи больше
Я полагаю, что после прочтения этой статьи некоторым из них может показаться, что автор слишком "критичен" и "критичен". Могут быть некоторые нетерпеливые люди, которые просто читают начало, а затем переходят к концу статьи, чтобы подготовиться к бою.
Согласны вы с автором или нет: «Утверждение, что куча — это область памяти, совместно используемая потоками, не совсем верно». На самом деле это не важно, важно то, что когда речь идет о памяти кучи, совместном использовании потоков и распределении памяти объектов, вы можете подумать о специальном TLAB, и это нормально.
Иногда самое страшное не в том, что ты не знаешь, а в том, что ты не знаешь, что ты не знаешь.
Кроме того, TLAB — это всего лишь схема оптимизации виртуальной машины HotSpot, и в спецификации виртуальной машины Java нет положений для TLAB. Поэтому не все виртуальные машины имеют эту функцию.
Обзор этой статьи основан на виртуальной машине HotSpot, Автор не намеренно «обобщает», а потому, что виртуальная машина HotSpot является самой популярной виртуальной машиной в настоящее время, и большинство случаев по умолчанию, когда мы ее обсуждаем, также Основано на HotSpot.
Эй, каждый раз, когда я пишу какие-то технические статьи, будет много людей, распыляющих его, и угол распыления какой-то странный, поэтому я должен сказать еще несколько слов, чтобы компенсировать это. В любом случае, любая форма обсуждения приветствуется, ведь даже если это распыление, оппонента может и не быть!