openjdk.ruOpenJDK на русском

JEP 312: Thread-Local Handshakes

Handshake-операции, локальные для потока

ОтветственныйRobbin Ehn
ТипFeature
ОбластьJDK
СтатусClosed / Delivered
Выпуск10
Компонентhotspot / runtime
Обсуждениеhotspot dash dev at openjdk dot java dot net
БлокируетJEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)
РецензентыMikael Vidstedt
ОдобренMikael Vidstedt
Создан2017/08/01 13:30
Обновлён2026/06/26 14:06
Задача8185640

Аннотация

Добавить способ выполнять обратный вызов в потоках без глобального safepoint виртуальной машины. Сделать возможной и дешёвой остановку отдельных потоков, а не только выбор между остановкой всех потоков или ни одного.

Что не является целью

Эффективно реализовать это на всех поддерживаемых архитектурах может оказаться невозможно. Поначалу поддержка всех процессорных архитектур и всех их версий не входит в цели.

Критерии успеха

  • Новый механизм не снижает производительность в стандартных бенчмарках больше чем на 1%.

  • Новый механизм не увеличивает время, необходимое для достижения традиционного глобального safepoint.

Мотивация

Возможность останавливать отдельные потоки можно применить во многих случаях:

  • Улучшить отзыв смещённых блокировок (biased lock revocation): останавливать для отзыва смещения только отдельные потоки, а не все.

  • Снизить общее влияние на задержку виртуальной машины от разных запросов обслуживания (serviceability), например получения трассировок стека всех потоков. Если в виртуальной машине много Java-потоков, такая операция может быть медленной.

  • Сделать выборку трассировок стека безопаснее за счёт меньшей зависимости от сигналов.

  • Убрать часть барьеров памяти с помощью так называемых приёмов Asymmetric Dekker Synchronization, выполняя handshake-операции с Java-потоками. Например, коду условной пометки карт (conditional card mark), который по своей природе нужен G1 и используется CMS, барьеры памяти не понадобятся. Благодаря этому можно оптимизировать барьер G1 после записи (post write barrier) и удалить ветвления, которые пытаются обойти барьер памяти.

Всё это поможет виртуальной машине добиться меньшей задержки за счёт уменьшения числа глобальных safepoint.

Описание

Handshake-операция — это обратный вызов, который выполняется для каждого JavaThread, пока этот поток находится в состоянии, безопасном с точки зрения safepoint. Обратный вызов выполняет либо сам поток, либо VM-поток, который при этом удерживает поток в заблокированном состоянии. Главное отличие safepoint от handshake-операции в том, что операция для каждого потока будет выполнена во всех потоках как можно скорее, и каждый поток продолжит работу, как только завершится его собственная операция. Если известно, что JavaThread выполняется, handshake-операцию можно провести и с этим единственным JavaThread.

В первой реализации будет ограничение: одновременно может выполняться не больше одной handshake-операции. Однако операция может охватывать любое подмножество всех JavaThread. VM-поток будет координировать handshake-операцию через VM-операцию, которая фактически не даст возникнуть глобальным safepoint во время handshake-операции.

Текущая схема safepoint изменяется: обращение идёт косвенно, через указатель, отдельный для каждого потока, и так можно заставить выполнение одного потока прерваться на защитной странице. По сути, всегда будут существовать две страницы опроса: одна всегда защищена, другая всегда не защищена. Чтобы заставить поток уступить выполнение, виртуальная машина меняет указатель соответствующего потока так, чтобы он указывал на защищённую страницу.

Поначалу handshake-операции, локальные для потока, будут реализованы на x64 и SPARC. На остальных платформах будут по-прежнему использоваться обычные safepoint. Новая штатная опция -XX:ThreadLocalHandshakes (значение по умолчанию true) позволяет пользователям выбрать обычные safepoint на поддерживаемых платформах.

Альтернативы

Рассматривались несколько альтернатив:

  • Генерировать вместо этого условные переходы. Это расходует состояние предсказателя ветвлений и не так компактно, как одна загрузка. Эксперименты в этой области показали, что производительность условных переходов может сильно зависеть от конкретной микроархитектуры целевого процессора. Ещё один недостаток подхода с условными переходами в том, что для каждого safepoint с условным переходом нужно генерировать соответствующую заглушку, которая обеспечивает возврат к месту опроса.

  • Есть идея, которая требует пожертвовать ещё одним регистром и затем загружать в сам регистр значение по адресу, хранящемуся в этом регистре, при условии что в регистре лежит адрес его собственного поля, локального для потока. Handshake-операция, локальная для потока, начиналась бы с того, что поле меняется на NULL. При следующем опросе регистр получил бы значение NULL, а при втором опросе загрузка вызвала бы прерывание. Это требует глобально пожертвовать регистром, прерывания обходятся дороже, и после запроса на остановку потока для достижения safepoint в среднем понадобится вдвое больше опросов. Преимущество в том, что теоретически это меньше влияет на выполнение приложения.

  • Ранее был создан прототип, в котором глобальная страница опроса оставалась как есть, но в коде виртуальной машины перехватывались только действительно целевые потоки. Потоки, не являющиеся целями handshake-операции, просто возвращались из обработчика сигнала и продолжали выполнение. Недостаток этого подхода в том, что если целевой поток отвечает медленно, это может вызвать шквал сигналов для других Java-потоков, так как страницу опроса нельзя снять с защиты, пока целевой поток не ответит.