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

JEP draft: Internal Frozen Arrays

Внутренние frozen-массивы (замороженные массивы)

ОтветственныйJohn Rose
ТипFeature
ОбластьJDK
СтатусDraft
Компонентhotspot / compiler
Создан2021/02/03 23:02
Обновлён2024/06/06 15:45
Задача8261099

Summary

Дать доверенным разработчикам реализации внутри JDK возможность блокировать выбранные массивы так, чтобы JVM отклоняла любые последующие записи в них.

Goals

Этот JEP позволяет Java Virtual Machine обрабатывать экземпляры массивов с особой пометкой как доступные только для чтения. Так называемый «frozen array» (замороженный массив) может иметь любой тип, длину и содержимое, но его элементам никогда нельзя присвоить новые значения. Frozen-массивы будут поддерживать некоторые оптимизации и приёмы обеспечения безопасности (например, свёртку констант и защитное копирование), которые не могут поддерживать обычные изменяемые массивы.

Это закрытая возможность внутри HotSpot и модуля java.base. Она предназначена для внутренних оптимизаций, а также служит примитивом для реализации видимых пользователю frozen-массивов.

Non-Goals

Этот JEP не предлагает изменений в языке программирования Java. Изменения в спецификации языка Java, которые допускают существование frozen-массивов, рассматриваются в отдельном последующем JEP, JDK-8261007. Построение полной пользовательской модели для frozen-массивов также откладывается до этого JEP.

Поскольку frozen-массивы нельзя изменять, они не соответствуют языку Java (пока язык не будет изменён так, чтобы их допускать), и их нельзя открывать ни через какой стандартный API, пока не будут внесены соответствующие изменения. Поэтому этот JEP не меняет ни одну точку стандартного API так, чтобы через неё открывались объекты frozen-массивов.

Motivation

Это первый шаг к видимым пользователю frozen-массивам. Полное обоснование см. в JDK-8261007.

Description

Определить три новых защищённых метода в jdk.internal.misc.Unsafe:

/**
 * Returns true if the given object is a frozen array.
 * The reference must be non-null and to an array.
 */
public boolean isFrozenArray(Object array) ...

/**
 * Freeze the given array object.
 * The reference must be non-null and to an array
 * created by {@code makeLarvalArray} and
 * which has not yet been frozen.
 * The returned object is the same as the input
 * array, which has been frozen, or perhaps a copy
 * with the same type and contents.
 *
 * The input array must not be used an any way
 * (whether loads, stores, re-freezing, or anything
 * else) after the call returns.  User should
 * Operations on the returned object must proceed as
 * if it were a fresh copy of the input array, even if
 * (as is likely in many cases but not all) the JVM
 * manages to recycle the object identity of the input array.
 *
 * The user must not rely on the JVM to actually check these
 * restrictions, which is why this method is unsafe.
 *
 * JIT IR representations should treat the input and output
 * values as distinct names.
 */
public Object freezeLarvalArray(Object array) ...

/**
 * Create a new array of the given class and length,
 * with all elements initialized to their default values.
 * This array must not made visible to other threads,
 * or synchronized on.  It may be passed as an input
 * to {@code freezeLarvalArray}.
 *
 * The user must not rely on the JVM to actually check these
 * restrictions, which is why this method is unsafe.
 */
public Object makeLarvalArray(Class<?> arrayClass, int length) ...

Это первый шаг к видимым пользователю frozen-массивам. Полное описание невнутренних API и сценариев использования см. в JDK-8261007.

Добавлять эти методы в Unsafe представляется наилучшим вариантом по следующим причинам:

  • На самом деле они небезопасны при состоянии гонки, поскольку массив может одновременно менять состояние так, что это противоречит намерению одного потока заблокировать массив.

  • Они также небезопасны, потому что их можно применить к «несотрудничающим» массивам и тем самым вызвать потенциально опасные сбои (отказ в обслуживании), когда затронутые массивы позже используются другими модулями.

  • Их нужно «держать под строгим контролем», пока не будет внедрена более безопасная пользовательская модель.

  • Любой нынешний пользователь Unsafe, скорее всего, «подглядывает и копается» в массивах, и поэтому его необходимо доработать так, чтобы он учитывал неизменяемость frozen-массивов. Если поместить эти методы в новый API с ограниченным доступом, найти и исправить затронутый код, использующий Unsafe, станет несколько сложнее.

Alternatives

Мы могли бы сразу ввести frozen-массивы в язык вместе с пользовательской моделью. Этот JEP предлагает более постепенный подход: проверить внутренние сценарии использования и оптимизации, прежде чем открывать эту возможность пользователям.

Сейчас отсутствие свёртки констант обходится с помощью внутренней аннотации @jdk.internal.vm.annotation.Stable. (На самом деле frozen-массивы, скорее всего, будут использовать некоторые из тех же приёмов JIT-оптимизации, что и stable-массивы.) Мы могли бы и дальше полагаться на эту возможность, без frozen-массивов. Но у этого есть две проблемы. Во-первых, stable-массивы плохо совместимы с null, что иногда создаёт неудобства для (внутренних) пользователей. Во-вторых, у stable-массивов пока нет общепринятой безопасной пользовательской модели (которая, вероятно, будет у frozen-массивов), поэтому stable-массивы могут так никогда и не стать публичной возможностью.

Это первый шаг к видимым пользователю frozen-массивам. Полное описание невнутренних API и сценариев использования см. в JDK-8261007.

Risks and Assumptions

Дополнительные проверки при записи в массивы требуют тщательной работы над оптимизатором, аналогичной удалению проверок нулевых указателей. Они могут вызвать проблемы с производительностью.

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

Dependencies and Future Work

Зависимостей от существующих работ нет.

Часть инженерных решений для проверок при записи в массивы в проекте Valhalla может быть применима и к проверкам изменяемости при записи, которые требуются этому JEP.

Это первый шаг к видимым пользователю frozen-массивам. См. JDK-8261007: он зависит от низкоуровневых внутренних примитивов, определённых в этой работе.