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: он зависит от низкоуровневых внутренних примитивов, определённых в этой работе.