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

JEP 178: Statically-Linked JNI Libraries

статически скомпонованные библиотеки JNI

ОтветственныйBob Vandette
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск8
Компонентcore-libs
Обсуждениеjdk8 dash dev at openjdk dot java dot net
ТрудоёмкостьS
ДлительностьS
РецензентыAlan Bateman, Alex Buckley
ОдобренMark Reinhold
Создан2013/02/18 20:00
Обновлён2016/06/07 16:40
Задача8046168

Аннотация

Расширить спецификацию JNI, чтобы она поддерживала статически скомпонованные нативные библиотеки.

Цели

  1. Изменить спецификацию Java SE и JDK так, чтобы разработчики могли упаковать среду выполнения Java, нативный код приложения и Java-код приложения в один исполняемый двоичный файл, которому не нужны разделяемые нативные библиотеки.

  2. Не требовать изменений в существующем Java-коде, чтобы использовать статическую нативную библиотеку вместо динамической. В частности, вызов метода вида System.loadLibrary("foo") должен загружать библиотеку "foo" независимо от того, предоставлена ли она в статической или в динамической форме.

  3. Позволить Java-приложению использовать сочетание статических и динамических нативных библиотек, при этом статические библиотеки должны находиться в памяти до любой попытки их использовать.

  4. Позволить при необходимости статически компоновать Java-агенты JVMTI со средами выполнения Java.

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

Сохранение полной совместимости нативного исходного кода на C/C++ для существующих динамических нативных библиотек, переведённых в статическую форму, не является целью. Существующие использования функций JNI_OnLoad и JNI_OnUnLoad придётся изменить, чтобы несколько статических библиотек могли сосуществовать.

Мотивация

Есть два основных сценария, в которых статические библиотеки JNI могут быть полезны:

  1. Нативным приложениям, встраивающим JRE, может потребоваться статически скомпонованный код JNI вместо динамически компонуемых библиотек.

  2. Java-приложениям, работающим в средах, которые ограничивают разделяемые библиотеки или не поддерживают их, нужно, чтобы JRE и весь код её нативных библиотек API были скомпонованы в один исполняемый файл.

Дополнительное преимущество: со статически скомпонованными библиотеками JNI компоновщик объектных файлов может оптимизировать весь исполняемый файл и, возможно, уменьшить его размер.

Описание

Чтобы добавить поддержку статических библиотек JNI, нужно решить две основные проблемы:

  1. Текущий Java API, который запускает процесс загрузки динамической библиотеки, нужно расширить для поддержки встроенных статических библиотек. Java-приложению, использующему статическую библиотеку JNI, нужен способ сообщить VM, что код библиотеки уже включён в образ приложения. В этом случае запрос System.loadLibrary для статической библиотеки должен пропускать обычный платформенно-зависимый процесс динамической загрузки.

    Текущая спецификация JNI упоминает такую поддержку, но VM HotSpot такое поведение не реализует.

  2. Интерфейс функций JNI_OnLoad и JNI_OnUnload нужно расширить для поддержки имён, специфичных для библиотеки, поскольку в приложении может существовать только одна функция с данным именем. Это можно реализовать, добавляя имя библиотеки к этим общеизвестным именам. Например, libnet.so может использовать JNI_OnLoad_net, JNI_OnUnload_net.

Эта возможность требует изменений как в API загрузки библиотек Java SE, так и в спецификации JNI. Ниже приведён первоначальный черновик изменений спецификации в обеих областях.

Изменения в Java API

Спецификации методов java.lang.System.load и java.lang.Runtime.load будут изменены так:

Загружает нативную библиотеку, указанную аргументом filename. Аргумент filename должен быть абсолютным путём.

Если аргумент filename после удаления платформенно-зависимого префикса библиотеки, пути и расширения файла указывает на библиотеку с именем L и нативная библиотека с именем L statically linked с VM, то вместо попытки загрузить динамическую библиотеку вызывается экспортируемая библиотекой функция JNI_OnLoad_L. Файл, соответствующий аргументу, не обязан существовать в файловой системе. Подробности см. в спецификации JNI.

В противном случае аргумент filename отображается на образ нативной библиотеки способом, зависящим от реализации.

Спецификации того, когда эти методы выбрасывают UnsatisfiedLinkError, будут изменены так:

UnsatisfiedLinkError — если filename не является абсолютным путём, нативная библиотека не statically linked с VM или хост-система не может отобразить библиотеку на образ нативной библиотеки.

Спецификации методов java.lang.System.loadLibrary и java.lang.Runtime.loadLibrary будут изменены так:

Загружает нативную библиотеку, указанную аргументом libname. libname не должен содержать платформенно-зависимого префикса, расширения файла или пути.

Если нативная библиотека с именем libname statically linked с VM, то вызывается экспортируемая библиотекой функция JNI_OnLoad_libname. Подробности см. в спецификации JNI.

В противном случае libname загружается из системного расположения библиотек и отображается на образ нативной библиотеки способом, зависящим от реализации.

Спецификации того, когда эти методы выбрасывают UnsatisfiedLinkError, будут изменены так:

UnsatisfiedLinkError — если аргумент libname содержит путь к файлу, нативная библиотека не statically linked с VM или хост-система не может отобразить библиотеку на образ нативной библиотеки.

Изменения в спецификации JNI

  • Нативная библиотека может быть statically linked с VM. Способ объединения библиотеки и образа VM зависит от реализации.

  • Чтобы эта библиотека считалась загруженной, вызов System.loadLibrary или эквивалентного API должен завершиться успешно.

  • Библиотека L, образ которой объединён с VM, считается statically linked тогда и только тогда, когда библиотека экспортирует функцию с именем JNI_OnLoad_L.

  • Если statically linked библиотека L экспортирует функцию с именем JNI_OnLoad_L и функцию с именем JNI_OnLoad, функция JNI_OnLoad будет проигнорирована.

  • Если библиотека L statically linked, то при первом вызове System.loadLibrary("L") или эквивалентного метода будет вызвана функция JNI_OnLoad_L с теми же аргументами и ожидаемым возвращаемым значением, что указаны для функции JNI_OnLoad.

  • Если библиотека L statically linked, то динамически компоновать библиотеку с тем же именем будет запрещено.

  • Когда загрузчик классов, содержащий statically linked нативную библиотеку L, удаляется сборщиком мусора, VM вызовет функцию JNI_OnUnload_L этой библиотеки, если такая функция экспортируется.

  • Если statically linked библиотека L экспортирует функцию с именем JNI_OnUnLoad_L и функцию с именем JNI_OnUnLoad, функция JNI_OnUnLoad будет проигнорирована.

Версия спецификации JNI будет увеличена до JNI_VERSION_1_8. Статически скомпонованные библиотеки будут поддерживаться только в этой или более поздней версии.

Изменения в спецификации параметра командной строки JVMTI -agentlib

Описание параметра командной строки -agentlib в спецификации будет изменено в JDK 8 так:

Если аргумент library указывает на библиотеку с именем L и нативная библиотека с именем L statically linked с VM, то агент должен экспортировать функцию Agent_OnLoad_L. Библиотека, соответствующая аргументу, не обязана существовать в файловой системе. Эта функция Agent_OnLoad_L будет вызвана VM, как описано в спецификации JVMTI. options будут переданы функции Agent_OnLoad_L при её вызове.

В противном случае имя после -agentlib: — это имя загружаемой библиотеки. Поиск библиотеки, как её полного имени, так и расположения, выполняется платформенно-зависимым способом. Обычно agent-lib-name преобразуется в имя файла, специфичное для операционной системы. options будут переданы агенту при запуске. Например, если указан параметр -agentlib:foo=opt1,opt2, VM попытается загрузить разделяемую библиотеку foo.dll из системного PATH в WindowsTM или libfoo.so из LD_LIBRARY_PATH в операционной среде SolarisTM.

Изменения в спецификации параметра командной строки JVMTI -agentpath

Описание параметра командной строки -agentpath в спецификации будет изменено в JDK 8 так:

Если аргумент filename после удаления платформенно-зависимого префикса библиотеки, пути и расширения файла указывает на library с именем L и нативная библиотека с именем L statically linked с VM, то агент должен экспортировать функцию Agent_OnLoad_L. Файл, соответствующий аргументу, не обязан существовать в файловой системе. Эта функция Agent_OnLoad_L вызывается VM, как описано в спецификации JVMTI. options будут переданы функции Agent_OnLoad_L при её вызове.

В противном случае путь после -agentpath: — это абсолютный путь, по которому загружается библиотека. Преобразование имени библиотеки не выполняется. options будут переданы агенту при запуске. Например, если указан параметр -agentpath:/myLibs/foo.so=opt1,opt2, VM попытается загрузить разделяемую библиотеку /myLibs/foo.so.

Изменения в спецификации нативного интерфейса JVMTI

  • Нативный агент JVMTI может быть statically linked с VM. Способ объединения библиотеки и образа VM зависит от реализации.

  • Агент L, образ которого объединён с VM, считается statically linked тогда и только тогда, когда агент экспортирует функцию с именем Agent_OnLoad_L.

  • Если statically linked агент L экспортирует функцию с именем Agent_OnLoad_L и функцию с именем Agent_OnLoad, функция Agent_OnLoad будет проигнорирована.

  • Если агент L statically linked, будет вызвана функция Agent_OnLoad_L с теми же аргументами и ожидаемым возвращаемым значением, что указаны для функции Agent_OnLoad.

  • Если агент L statically linked, агент с тем же именем нельзя будет загрузить динамически.

  • VM вызовет функцию Agent_OnUnload_L агента, если такая функция экспортируется, в тот же момент запуска, в который она вызвала бы динамическую точку входа Agent_OnUnLoad.

  • Если statically linked агент L экспортирует функцию с именем Agent_OnUnLoad_L и функцию с именем Agent_OnUnLoad, функция Agent_OnUnLoad будет проигнорирована.

  • Если агент L statically linked, будет вызвана функция Agent_OnAttach_L с теми же аргументами и ожидаемым возвращаемым значением, что указаны для функции Agent_OnAttach.

  • Если statically linked агент L экспортирует функцию с именем Agent_OnAttach_L и функцию с именем Agent_OnAttach, функция Agent_OnAttach будет проигнорирована.

com.sun.tools.attach.VirtualMachine.loadAgentLibrary

В javadoc этого метода будет добавлен следующий текст:

Если агент statically linked с VM, которая в ином случае загрузила бы его, то имя вызываемой функции Agent_OnAttach будет специфичным для библиотеки, как определено в разделе спецификации JVMTI о -agentlib.

com.sun.tools.attach.VirtualMachine.loadAgentPath

В javadoc этого метода будет добавлен следующий текст:

Если агент statically linked с VM, которая в ином случае загрузила бы его, то имя вызываемой функции Agent_OnAttach будет специфичным для библиотеки, как определено в спецификации JVMTI о -agentpath.

Версия спецификации JVMTI будет увеличена до JDK18_JVMTI_VERSION.
Значением JDK18_JVMTI_VERSION будет 0x30010203, что соответствует версии 1.2.3.

Эта новая функциональность будет поддерживаться в виртуальных машинах, поддерживающих JDK18_JVMTI_VERSION или более позднюю версию.

Влияние

  • Совместимость: эта новая функциональность не должна влиять на существующие динамические библиотеки.
  • Переносимость: при статической сборке нативного исходного кода JNI требуется изменить имена функций.
  • TCK: тесты нативных библиотек JNI нужно будет адаптировать для проверки поддержки статически скомпонованных нативных библиотек.
  • TCK: тесты агентов JVMTI нужно будет адаптировать для проверки поддержки статически скомпонованных библиотек агентов.