
Что нового в Java 24. Часть 2
Это вторая часть обзора JEP в Java 24. Если вы пропустили первую часть, см. Что нового в Java. Часть 1.
В этой части мы рассмотрим ещё восемь фичей, среди которых Flexible Constructor Bodies, Simple Source Files и Instance Main Methods и другие.
Содержание
- Как начать использовать фичи в предварительной и экспериментальной версиях
- Новые фичи
- Улучшенные фичи
- Удалённые и deprecated фичи
- JEP 501: 32-битный порт x86-систем устарел и будет удалён {#jep-501:-32-битный-порт-x86-систем-устарел-и-будет-удалён}
Как начать использовать фичи в предварительной и экспериментальной версиях
Чтобы попробовать экспериментальные фичи или фичи в предварительной версии, нужно их явно включить. Вы можете это сделать как через командную строку, так и настроить в интегрированной среде разработки (IDE). Некоторые фичи нужно включать на уровне javac
, а не только при запуске java
.
В командной строке включите фичу в предварительной версии одним из следующих способов:
- Скомпилируйте программу с помощью
javac --release 24 --enable-preview Main.java
и запустите ее с помощьюjava --enable-preview Main
. - При использовании source code launcher запустите программу с
java --enable-preview Main.java
. - При использовании jshell запустите его с помощью
jshell --enable-preview
.
Новые фичи
JEP 475: Late Barrier Expansion for G1
JEP 475 вводит Late Barrier Expansion для сборщика мусора G1. Эта оптимизация поможет повысить производительность Java-приложений в облачной среде за счёт уменьшения времени работы CPU и снижения накладных расходов на память во время прогрева JVM.
Чтобы понять значимость этого улучшения, нужно разобраться, что такое барьер сборщика мусора (GC barrier) и как он увеличивает накладные расходы JIT-компилятора в JVM.
JIT-компилятор транслирует байткод Java в машинный код, используя концепцию sea of nodes, представляющую собой граф программной зависимости. В процессе компиляции компилятор обходит этот граф, применяет оптимизации к узлам, а на поздних стадиях компиляции заменяет узлы барьеров GC на реальные инструкции.
Барьеры GC контролируют доступ приложения к памяти и отслеживают её изменения, необходимые для корректной работы сборщика мусора. JIT-компилятор обрабатывает узлы барьеров на ранних стадиях компиляции, как и любые другие узлы. Это приводит к значительным накладным расходам. Кроме того, некоторые оптимизации могут нарушать порядок барьеров, создавая сложные проблемы.
Идея Late Barrier Expansion G1 GC заключается в том, чтобы откладывать внедрение барьеров до финального этапа компиляции — генерации машинного кода. При этом инструкции доступа к памяти, зависящие от GC, преобразуются в машинный код с учётом информации о барьерах, которая была добавлена на ранних стадиях компиляции. Такой подход позволяет генерировать код, сопоставимый по качеству с кодом, оптимизированным компилятором C2, но при этом снижает нагрузку на компилятор C2.
Сборщик мусора ZGC успешно использует Late Barrier Expansion, начиная с JDK 14. Этот механизм стал одним из ключевых факторов его стабильности. В связи с этим было принято решение применить аналогичный подход и к G1 GC, повторно использовав многие механизмы, разработанные для ZGC.
JEP 493: Linking Run-Time Images without JMODs
Оптимизированная работа jlink позволяет создавать нестандартный JRE без использования JMOD-файлов, что уменьшает размер JDK примерно на 25%. Эта возможность должна быть активирована на этапе сборки JDK и выключена по умолчанию.
Цель этого JEP — обеспечить возможность создания минимального окружения выполнения (run-time image) независимо от того, представлены ли модули в формате JMOD, модульных JAR-файлах или уже связаны в существующем run-time image.
В облачных средах, где контейнерные образы с JDK передаются по сети, важно минимизировать их размер. Полноценный JDK содержит run-time image (саму среду выполнения) и JMOD-файлы (архивированные модули JDK). Однако JMOD-файлы фактически дублируют содержимое run-time image и занимают около 25% от общего размера JDK. Если jlink сможет извлекать файлы из run-time image, JMOD-файлы можно будет исключить и значительно сократить размер JDK.
JEP 493 вводит новый флаг сборки: --enable-linkable-runtime
, который:
-
Позволяет jlink работать без JMOD-файлов.
-
Исключает каталог jmods из финального JDK, уменьшая его размер на 25%.
-
Сохраняет полный набор модулей, несмотря на отсутствие JMOD-файлов.
Пример сборки JDK с этой опцией:
$ configure [ ... other options ... ] --enable-linkable-runtime
$ make images
Теперь, если вы запустите jlink в JDK 24 с опцией --enable-linkable-runtime
, то jlink:
-
Предпочтёт брать модули из JMOD-файлов (если они есть).
-
Если JMOD отсутствуют, извлечёт модули непосредственно из run-time image.
-
Потребует явного указания всех модулей через
--module-path
, кроме модуляjava.base
(он включается автоматически).
Пример работы:
$ jlink --help
Usage: jlink <options> --module-path <modulepath> --add-modules <module>[,<module>...]
...
Capabilities:
Linking from run-time image enabled
$
Это означает, что jlink может извлекать модули непосредственно из run-time image.
Если бы эта возможность не была включена, jlink вывел бы: Linking from run-time image disabled
.
Создадим минимальный run-time image только с java.xml
и java.base
:
$ jlink --add-modules java.xml --output image
$ image/bin/java --list-modules
java.base@24
java.xml@24
Полученный образ занимает на 60% меньше места, чем полный run-time image JDK.
Теперь пример посложнее: создадим run-time image для модуля app
, который зависит от lib
, используя модульные JAR-файлы в mlib
:
$ ls mlib
app.jar lib.jar
$ jlink --module-path mlib --add-modules app --output app
$ app/bin/java --list-modules
app
lib
java.base@24
Здесь jlink:
-
Берёт
app
иlib
изmlib
. -
Извлекает модули JDK из текущего run-time image.
Для анализа источников модулей можно использовать --verbose
:
$ jlink --add-modules foo \
--module-path=custom-jmods \
--verbose \
--output foo-image
Linking based on the current run-time image
java.base jrt:/java.base (run-time image)
foo file:///path/to/custom-jmods/foo.jmod
Здесь java.base
взят из run-time image, а foo
из JMOD-файла.
Ограничения и исключения:
-
По умолчанию отключено: Производители JDK могут решать, включать ли эту возможность в свои дистрибутивы. Мы решили не включать. Ниже объясним почему.
-
jlink не может создавать образы, включающие сам
jlink
(jdk.jlink
), если JMOD-файлы отсутствуют.
$ jlink --add-modules jdk.jlink --output image
Error: This JDK does not contain packaged modules and cannot create a run-time image including jdk.jlink
- Если изменены конфигурационные файлы (например,
conf/security/java.security
), jlink откажется создавать образ:
$ jlink --add-modules java.xml --output image
Error: [...]/bin/conf/security/java.security has been modified
-
Это предотвращает создание образов с потенциально небезопасной конфигурацией.
-
Кросс-компиляция (например, создание Windows-образа на Linux) невозможна.
-
Нельзя использовать
--patch-module
для модификации run-time image передjlink
. -
Нельзя извлекать модули из другого run-time image, передавая его в
--module-path
.
При сборке образов Axiom JDK этот JEP не будет включен, поскольку образы и инструмент jlink имеют ограничения, описанные в самом JEP 493.
В частности, невозможно создать среду выполнения для архитектур, отличных от той, на которой работает образ. Также невозможно создать образы, содержащие jlink. Кроме того, работа с образами будет невозможна, если будут изменены файлы из комплекта JDK, предназначенные для пользовательской настройки.
В Axiom JDK есть Lite-образы, которые отличаются меньшим статическим потреблением памяти и оптимизированы по размеру инсталляторов и установленных дистрибутивов. Мы сравнили размеры инсталляторов (tar.gz) Lite-версии и инсталлятор с включенным JEP 493: размер инсталлятора с включенным JEP 493 — примерно 157 МБ, инсталлятор Axiom JDK Lite — 79 МБ.
Улучшенные фичи
JEP 492: Flexible Constructor Bodies (третья предварительная версия)
В Java конструкторы теперь могут содержать операторы перед явным вызовом конструктора (super(..)
или this(..)
). Эти операторы не могут ссылаться на создаваемый экземпляр, но могут инициализировать его поля. Это делает классы более надёжными при переопределении методов. Новая возможность представлена в предварительной версии в JDK 22 (JEP 447) и повторно в JDK 23 (JEP 482). В JDK 24 предлагается её третье превью без особых изменений.
Цели создания Flexible Constructor Bodies:
-
Дать разработчикам возможность размещать код инициализации в конструкторе, а не выносить его в вспомогательные методы.
- Разделить тело конструктора на две части:
- Пролог: код, выполняемый перед вызовом родительского конструктора.
- Эпилог: код, выполняемый после вызова родительского конструктора.
- Гарантировать, что код в конструкторе подкласса не нарушает процесс создания объекта суперкласса.
Конструкторы отвечают за создание корректных объектов. Например, если у класса Person
есть поле age
, оно не должно быть отрицательным. Проверка аргументов в конструкторе позволяет защититься от некорректных значений.
Однако в текущей модели Java вызов super(..)
должен быть первой инструкцией в конструкторе. Это накладывает ограничения на валидацию и подготовку аргументов перед передачей в суперкласс.
Например, иногда нужно валидировать аргумент перед передачей его конструктору суперкласса.
До Flexible Constructor Bodies, валидация аргумента проводилась после вызова super(..)
:
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
super(value); // Потенциально ненужные вычисления
if (value <= 0) throw new IllegalArgumentException("Value must be positive");
}
}
Чтобы избежать ненужных вычислений, приходилось выносить проверку в статический метод, используя обходные пути:
public class PositiveBigInteger extends BigInteger {
private static long verifyPositive(long value) {
if (value <= 0) throw new IllegalArgumentException("Value must be positive");
return value;
}
public PositiveBigInteger(long value) {
super(verifyPositive(value));
}
}
С Flexible Constructor Bodies код можно упростить следующим образом:
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
if (value <= 0) throw new IllegalArgumentException("Value must be positive");
super(value);
}
}
Если аргумент конструктора требует сложной обработки, раньше приходилось использовать статический метод:
public class Sub extends Super {
private static byte[] prepareByteArray(Certificate certificate) {
var publicKey = certificate.getPublicKey();
if (publicKey == null) throw new IllegalArgumentException("Invalid certificate");
return switch (publicKey) {
case RSAKey rsaKey -> ...;
case DSAPublicKey dsaKey -> ...;
default -> ...;
};
}
public Sub(Certificate certificate) {
super(prepareByteArray(certificate));
}
}
Теперь обработку можно делать прямо в конструкторе:
public Sub(Certificate certificate) {
var publicKey = certificate.getPublicKey();
if (publicKey == null) throw new IllegalArgumentException("Invalid certificate");
byte[] certBytes = switch (publicKey) {
case RSAKey rsaKey -> ...;
case DSAPublicKey dsaKey -> ...;
default -> ...;
};
super(certBytes);
}
Раньше при повторном использовании аргументов конструктора суперкласса приходилось использовать вспомогательные конструкторы:
public class Super {
public Super(C x, C y) { ... }
}
public class Sub extends Super {
private Sub(C x) { super(x, x); }
public Sub(int i) { this(new C(i)); }
}
Теперь можно делать это прямо в конструкторе:
public class Sub extends Super {
public Sub(int i) {
var x = new C(i);
super(x, x);
}
}
Ранее вызовы конструкторов выполнялись строго сверху вниз:
D
--> C
--> B
--> A
--> Object constructor body
--> A constructor body
--> B constructor body
--> C constructor body
D constructor body
Теперь сначала выполняются прологи (снизу вверх), а затем эпилоги (сверху вниз):
D prologue
--> C prologue
--> B prologue
--> A prologue
--> Object constructor body
--> A epilogue
--> B epilogue
--> C epilogue
D epilogue
У Flexible Construction Bodies есть технические ограничения:
-
В прологе запрещено использовать
this
иsuper
, но разрешено инициализировать поля класса. -
В эпилоге разрешено использовать
this
и обращаться к полям. -
Поля суперкласса нельзя изменять в прологе.
-
Разрешена ранняя инициализация полей перед
super()
, если у поля нет явного инициализатора.
Проблема переопределения методов в конструкторах:
class Super {
Super() { overriddenMethod(); }
void overriddenMethod() { System.out.println("hello"); }
}
class Sub extends Super {
final int x;
Sub(int x) {
/* super(); */
this.x = x;
}
@Override
void overriddenMethod() { System.out.println(x); }
}
new Sub(42)
выведет 0, так как Super()
вызывает переопределённый метод, а поле x
ещё не проинициализировано.
Теперь это решается так:
class Sub extends Super {
final int x;
Sub(int x) {
this.x = x; // Ранняя инициализация перед super()
super();
}
@Override
void overriddenMethod() { System.out.println(x); }
}
Теперь new Sub(42)
корректно выведет 42.
Новое поведение конструкторов делает код более читаемым и надёжным:
-
Упрощает валидацию параметров.
-
Позволяет готовить аргументы перед вызовом
super(..)
. -
Улучшает управление порядком инициализации.
-
Устраняет необходимость во вспомогательных методах и конструкторах.
Функция доступна в JDK 24 в режиме предварительной версии, поэтому её нужно явно включить.
JEP 495: Simple Source Files и Instance Main Methods (четвёртая предварительная версия)
Java используется для разработки сложных приложений, поддерживаемых в течение многих лет большими командами. Она предоставляет механизмы инкапсуляции, управления пространствами имен и модульности, что важно для организации кода. Однако язык также позиционируется как первый для изучения программирования, но его сложный синтаксис мешает новичкам.
Простые программы, такие как "Hello, World!"
, содержат лишний код, который пугает новичков:
-
public class
,public static void main(String[] args)
— избыточны на первых этапах. -
String[] args
не используется, но сбивает с толку. -
static
требует объяснения концепции статических и экземплярных методов. -
System.out.println
кажется сложным для простого вывода на экран.
Цель изменений — упростить написание небольших программ, вводя концепции последовательно и не требуя знаний о классах и модулях с самого начала. Из нововведений в этом JEP по сравнению с предыдущей версией — новая терминология и пересмотренное название, в остальном — без изменений.
C JEP 495 в Java изменится следующее:
- main-метод будет нестатическим, что позволяет убрать
public static void main(String[] args)
и заменить его на:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
- Введение простых файлов исходного кода без явного объявления класса:
void main() {
System.out.println("Hello, World!");
}
- Автоматический импорт полезных методов для ввода-вывода, заменяющий сложный для восприятия
System.out.println
наprintln
:
void main() {
println("Hello, World!");
}
- Автоматический импорт стандартных API, например,
java.util
иjava.io
по аналогии сjava.lang
. Это устранит необходимость явно указыватьimport
.
Эти изменения находятся в предварительной версии в JDK 24. Чтобы попробовать эти нововведения, явно включите их.
Эта фича упрощает написание небольших программ, сохраняя при этом совместимость с Java. Новичкам будет проще изучать язык без сложного синтаксиса на ранних этапах. Разработчики смогут быстрее писать скрипты, прототипы и вспомогательные утилиты.
Удалённые и deprecated фичи
JEP 479: Удаление 32-битного порта для Windows
Ещё в JDK 21 32-битный порт под Windows пометили как подлежащий удалению (deprecated). Теперь в Java 24 исходный код и инфраструктура сборки для поддержки Windows x86 (32 бит) полностью удалены.
Поддержка последней версии Windows с поддержкой 32 бит — Windows 10 — закончится в октябре 2025. Это одна из причин окончания поддержки порта в JDK. Следующая причина — реализация виртуальных потоков (JEP 436) для Windows x86 (32-бит) не приносит ожидаемых преимуществ Project Loom: виртуальные потоки в этой версии реализованы через потоки ядра (kernel threads).
Удаление порта позволит ускорить разработку Java-платформы, новых возможностей в JDK, а также упростит поддержку актуальных платформ.
Разработчикам, которые всё ещё используют 32-битную Java на Windows для приложений, потребуется либо перейти на 64-битную JDK и 64-битную Windows или использовать старые версии JDK (до JDK 23). Поддержка дистрибьюторов и поставщиков JDK при миграции критически важна.
JEP 486: Выключение Security Manager
Механизм Security Manager уже давно не является основным способом защиты клиентского Java-кода, практически не используется для защиты серверных приложений и требует значительных затрат на поддержку. По этой причине уже в JDK 17 Security Manager был помечен как подлежащий удалению.
Security Manager существовал с первой версии Java и реализовывал принцип наименьших привилегий: по умолчанию код не имел доступа к ресурсам (файловая система, сеть и т. д.), а разрешения задавались вручную. Однако на практике модель привилегий слишком сложна, Security Manager всегда был отключен по умолчанию и его использовали крайне редко.
Кроме того, поддержка Security Manager усложняла стандартные библиотеки Java:
-
1000+ методов проверяли разрешения перед доступом к ресурсам. Например, конструкторы класса
FileOutputStream
обращались к Security Manager, который применяет сложный алгоритм для определения разрешения доступа. -
1200+ методов повышали свои привилегии при работе (пример:
LocalDateTime.now()
считывает файл временных зон JDK, даже если приложение не имеет разрешений). -
Любые изменения в API требовали тщательного аудита, несмотря на низкий уровень использования Security Manager.
-
Многие приложения просто давали полные разрешения коду, теряя смысл модели наименьших привилегий.
Так, Security Manager объявили устаревшим в Java 17 (JEP 411) и его включение стало сопровождаться предупреждениями о будущем удалении.
Разработчики и компании активно переходили с Java 8 и 11 на Java 17 и выше. Java-фреймворки вовсе отказались от поддержки Security Manager (Derby, Ant, SpotBugs, Tomcat). Jakarta EE удалила требование поддержки Security Manager. Новые проекты вообще не использовали Security Manager.
В рамках JEP 486 внесены следующие изменения:
- Невозможно включить Security Manager при старте JVM (
-Djava.security.manager
). - Невозможно установить кастомный Security Manager во время выполнения (
System.setSecurityManager
). - API Security Manager недоступен: его методы либо возвращают
null
, либо выбрасываютUnsupportedOperationException
. - Удален файл конфигурации
java.policy
. - Свойства Java, связанные с Security Manager (
java.security.policy
,jdk.security.filePermCompat
), игнорируются. - Из спецификации удалят упоминания о
SecurityException
в 1000+ методах стандартных библиотек.
Если вы попытаетесь включить Security Manager, вы увидите следующую ошибку:
$ java -Djava.security.manager -jar app.jar
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security Manager. Enabling a Security Manager is not supported.
Вот такой код не будет работать:
System.setSecurityManager(new SecurityManager()); // выбросит UnsupportedOperationException
Удаление Security Manager освободит ресурсы для реализации более актуальных механизмов защиты:
- Новые протоколы (TLS 1.3, HTTP/3).
- Современные алгоритмы шифрования (SHA-3, EdDSA, RSASSA-PSS).
- Отключение устаревших криптографических алгоритмов.
- Поддержка постквантовой криптографии.
- Безопасная десериализация (deserialization filters в Java 9 и работа над новой моделью).
- Строгая обработка XML (в Java 23 можно заблокировать внешние DTD).
Security Manager не защищал от большинства современных угроз, поэтому его удаление улучшит безопасность платформы.
Security Manager использовался для sandboxing’а Java-кода, но только для апплетов. Для современных приложений рекомендуются внешние механизмы:
- Контейнеризация (Docker, Kubernetes).
- Гипервизоры.
- Механизмы ОС (macOS App Sandbox, Linux seccomp).
Эти технологии широко используются и проще в настройке, чем Security Manager.
Некоторые приложения использовали Security Manager не для обеспечения безопасности, а для перехвата вызовов API (например, блокировка System.exit()
).
Проблема в том, что Security Manager легко обходится вредоносным кодом. Для решения этой проблемы вместо Security Manager можно использовать агенты Java (Instrumentation API), статический анализ кода или байткод-инъекции.
Security Manager останется доступен в Java 23 и более ранних версиях.
Следующим шагом станет обновление спецификации Java Platform Specification, исключающее возможность включения Security Manager, а также удаление всех упоминаний о нём в стандартных классах платформы. Это изменение не затронет подавляющее большинство приложений, библиотек и инструментов.
Полное удаление Security Manager API запланировано в одном из будущих релизов.
JEP 490: ZGC: удаление режима non-generational
В Java 23 режим поколений (generational) в ZGC стал режимом по умолчанию. Чтобы снизить затраты на поддержку двух режимов и ускорить развитие новых возможностей, в JEP 490 весь код, связанный с режимом non-generational в ZGC, и тесты будут удалены.
В JEP 439 уже заявлено, что ZGC с поколениями является более предпочтительным вариантом для большинства сценариев. Отказ от non-generational режима позволит сократить затраты на долгосрочное сопровождение.
Что изменится:
- Опция
-XX:+ZGenerational
будет объявлена устаревшей (obsolete). - В следующем релизе HotSpot JVM перестанет распознавать эту опцию и не будет запускаться при её указании.
После удаления non-generational режима ZGC параметры командной строки будут работать следующим образом:
Опции JVM | Результат |
---|---|
-XX:+UseZGC |
Используется ZGC с поколениями |
-XX:+UseZGC -XX:+ZGenerational |
Используется ZGC с поколениями, выводится предупреждение о том, что опция -XX:+ZGenerational устарела. |
-XX:+UseZGC -XX:-ZGenerational |
Используется ZGC с поколениями, выводится предупреждение о том, что опция -XX:+ZGenerational устарела. |
После перехода на ZGC с поколениями возможны изменения в логах GC и данных, доступных через API управления и диагностики.
Этот JEP связан с рисками, описанными в JEP 439 и JEP 474. Основные риски:
-
Изменение производительности: некоторые специфические рабочие нагрузки, являющиеся по природе non-generational, могут испытывать небольшое снижение производительности. Однако доля таких случаев мала и не оправдывает сохранение двух режимов.
-
Корректировка конфигурации: пользователям, настроившив JVM под non-generational ZGC, может потребоваться внести изменения в конфигурацию.
-
Проблемы из-за предупреждений: сообщения о том, что опция
-XX:+ZGenerational
устарела, могут создавать неудобства. -
Изменение нагрузок: нагрузки, зависящие от внутренностей JVM, логов GC или данных, получаемых через интерфейсы управления, могут потребовать особого внимания.
Переход на ZGC с поколениями является логичным шагом для упрощения сопровождения JVM. Несмотря на возможные небольшие изменения в поведении ZGC, этот режим обеспечит более эффективную работу сборщика мусора в долгосрочной перспективе.
JEP 501: 32-битный порт x86-систем устарел и будет удалён {#jep-501:-32-битный-порт-x86-систем-устарел-и-будет-удалён}
В рамках JEP 501 32-битный порт x86-систем помечен как подлежащий удалению. В будущем планируется избавиться от него полностью. Единственный оставшийся 32-битный порт — Linux x86 — также попадёт под это изменение. После удаления, единственным способом запуска Java на 32-битных x86-процессорах останется архитектурно-независимый Zero-порт.
Поддержка 32-битного x86 требует много ресурсов и практическая ценность этого порта снижается, а преимущества его удаления перевешивают.
-
Современные технологии JDK, такие как Loom, Foreign Function & Memory API (FFM) и Vector API, сложно адаптировать под 32-битные платформы.
-
Поддержка 32-битных систем замедляет разработку новых возможностей.
-
Последняя версия Windows с поддержкой 32-битных систем (Windows 10) будет снята с поддержки в октябре 2025 года.
-
Дистрибутивы Linux постепенно отказываются от 32-битных версий (например, Debian).
ARM32 останется, но потребуется дополнительная работа для его поддержки после удаления 32-битного x86.
При попытке сборки JDK для 32-битных x86-систем будет выдаваться ошибка:
$ bash ./configure
...
configure: error: The 32-bit x86 port is deprecated and may be removed in a future release.
Однако можно временно обойти это ограничение с помощью флага --enable-deprecated-ports=yes
, но при этом выводится предупреждение:
$ bash ./configure --enable-deprecated-ports=yes
...
checking compilation type... native
configure: WARNING: The 32-bit x86 port is deprecated and may be removed in a future release.
...
Build performance summary:
* Cores to use: 32
* Memory limit: 96601 MB
The following warnings were produced. Repeated here for convenience:
WARNING: The 32-bit x86 port is deprecated and may be removed in a future release.
$
Подготовка к удалению 32-битного x86 порта позволит направить ресурсы на разработку новых функций, соответствующих требованиям современных 64-битных систем.