Собственный интерфейс Java
![]() | В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
В разработке программного обеспечения Java Native Interface ( JNI ) представляет собой интерфейса внешних функций программирования среду , которая позволяет коду Java , работающему на виртуальной машине Java (JVM), вызывать и вызываться [ 1 ] собственные приложения (программы, специфичные для аппаратной платформы и платформы операционной системы ) и библиотеки, написанные на других языках, таких как C , C++ и ассемблер .
Цели
[ редактировать ]JNI позволяет программистам писать собственные методы для обработки ситуаций, когда приложение не может быть полностью написано на языке программирования Java, например, когда стандартная классов библиотека Java не поддерживает функции, специфичные для платформы, или библиотеку программ. Он также используется для изменения существующего приложения (написанного на другом языке программирования), чтобы оно было доступно приложениям Java. Многие классы стандартной библиотеки зависят от JNI, предоставляющего функциональные возможности разработчику и пользователю, например возможности файлового ввода-вывода и звука. Включение реализаций API, чувствительных к производительности и платформе, в стандартную библиотеку позволяет всем приложениям Java получать доступ к этой функциональности безопасным и независимым от платформы способом.
Платформа JNI позволяет собственному методу использовать объекты Java так же, как эти объекты использует код Java. Собственный метод может создавать объекты Java, а затем проверять и использовать эти объекты для выполнения своих задач. Собственный метод также может проверять и использовать объекты, созданные кодом приложения Java.
Только приложения и подписанные апплеты могут вызывать JNI.
Приложение, использующее JNI, теряет возможности переносимости платформы, которые предлагает Java (частичным обходным решением является написание отдельной реализации кода JNI для каждой платформы, а Java определяет операционную систему и загружает правильную версию во время выполнения).
Собственный код может не только взаимодействовать с Java, но и опираться на Java. Canvas
, что возможно с помощью собственного интерфейса Java AWT . Процесс практически тот же, с небольшими изменениями. Собственный интерфейс Java AWT доступен только начиная с J2SE 1.3.
JNI также обеспечивает прямой доступ к ассемблерному коду , даже не проходя через C. мост [ 2 ] Доступ к Java-приложениям из сборки возможен таким же образом. [ 3 ]
Дизайн
[ редактировать ]В среде JNI собственные функции реализуются в отдельных файлах .c или .cpp. (C++ предоставляет немного более простой интерфейс с JNI.) Когда JVM вызывает функцию, она передает JNIEnv
указатель, а jobject
указатель и любые аргументы Java, объявленные методом Java. Например, следующее преобразует строку Java в собственную строку:
extern "C"
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj, jstring javaString)
{
const char *nativeString = env->GetStringUTFChars(javaString, 0);
//Do something with the nativeString
env->ReleaseStringUTFChars(javaString, nativeString);
}
The env
Указатель — это структура, содержащая интерфейс для JVM. Он включает в себя все функции, необходимые для взаимодействия с JVM и работы с объектами Java. Примерами функций JNI являются преобразование собственных массивов в/из массивов Java, преобразование собственных строк в/из строк Java, создание экземпляров объектов, выдача исключений и т. д. По сути, все, что может сделать код Java, можно сделать с помощью JNIEnv
, хотя и с гораздо меньшей легкостью.
Аргумент obj
является ссылкой на объект Java, внутри которого объявлен этот собственный метод.
Собственные типы данных могут быть сопоставлены с типами данных Java или из них. Для составных типов, таких как объекты, массивы и строки, собственный код должен явно преобразовывать данные, вызывая методы в JNIEnv
.
Указатель среды JNI ( JNIEnv* ) передается в качестве аргумента для каждой встроенной функции, сопоставленной с методом Java, что позволяет взаимодействовать со средой JNI внутри собственного метода. Этот указатель интерфейса JNI можно сохранить, но он остается действительным только в текущем потоке. Другие потоки должны сначала вызвать AttachCurrentThread(), чтобы подключиться к виртуальной машине и получить указатель интерфейса JNI. После подключения собственный поток работает как обычный поток Java, выполняющийся в собственном методе. Собственный поток остается подключенным к виртуальной машине до тех пор, пока она не вызовет DetachCurrentThread() для отсоединения. [ 4 ]
Платформа JNI не обеспечивает никакой автоматической сборки мусора для ресурсов памяти, отличных от JVM, выделяемых кодом, выполняющимся на собственной стороне. Следовательно, собственный код (например, язык ассемблера) берет на себя ответственность за явное освобождение любых таких ресурсов памяти, которые приобретает собственный код.
На платформах Linux и Solaris, если собственный код регистрируется как обработчик сигналов, он может перехватывать сигналы, предназначенные для JVM. Цепочку ответственности можно использовать, чтобы обеспечить лучшее взаимодействие машинного кода с JVM. На платформах Windows структурированная обработка исключений (SEH) может использоваться для упаковки собственного кода в блоки try/catch SEH, чтобы перехватывать программные прерывания, генерируемые машиной (CPU/FPU) (например, нарушения доступа к нулевому указателю и операции деления на ноль). ), и обрабатывать эти ситуации до того, как прерывание будет передано обратно в JVM (т. е. на сторону Java-кода), что, по всей вероятности, приведет к необработанному исключению. [ оригинальное исследование? ]
Кодировка, используемая для функций NewStringUTF, GetStringUTFLength, GetStringUTFChars, ReleaseStringUTFChars и GetStringUTFRegion, — «модифицированная UTF-8». [ 5 ] что не является действительным UTF-8 для всех входов, но на самом деле это другая кодировка. Нулевой символ (U+0000) и кодовые точки, не относящиеся к базовой многоязычной плоскости (больше или равные U+10000, т. е. представленные как суррогатные пары в UTF-16), в модифицированном UTF-8 кодируются по-разному. Многие программы на самом деле используют эти функции неправильно и обрабатывают строки UTF-8, возвращаемые или передаваемые в функции, как стандартные строки UTF-8, а не модифицированные строки UTF-8. Программы должны использовать функции NewString, GetStringLength, GetStringChars, ReleaseStringChars, GetStringRegion, GetStringCritical и ReleaseStringCritical, которые используют кодировку UTF-16LE в архитектурах с прямым порядком байтов и UTF-16BE в архитектурах с прямым порядком байтов, а затем использовать кодировку от UTF-16 до UTF- 8 процедур преобразования. [ оригинальное исследование? ]
Типы сопоставлений
[ редактировать ]В следующей таблице показано сопоставление типов между Java (JNI) и машинным кодом.
Тип С | Тип языка Java | Описание | Введите подпись |
---|---|---|---|
беззнаковый символ uint8_t |
jboolean | беззнаковые 8 бит | С |
подписанный символ int8_t |
jbyte | подписанные 8 бит | Б |
беззнаковый короткий uint16_t |
Джчар | беззнаковые 16 бит | С |
короткий int16_t |
jshort | подписанные 16 бит | С |
интервал int32_t |
шутка | подписанные 32 бита | я |
долго долго int64_t |
Джлонг | подписанные 64 бита | Дж |
плавать | jfloat | 32 бита | Ф |
двойной | jdouble | 64 бита | Д |
пустота | V |
Кроме того, подпись "L fully-qualified-class ;"
будет означать класс, однозначно определенный этим именем; например, подпись "Ljava/lang/String;"
относится к классу java.lang.String
. Кроме того, префикс [
в подпись составляет массив этого типа; например, [I
означает тип массива int. Наконец, void
подпись использует V
код.
Эти типы взаимозаменяемы. Можно использовать jint
где вы обычно используете int
и наоборот, без необходимости приведения типов . Однако сопоставление строк и массивов Java с собственными строками и массивами отличается. Если jstring
используется там, где char *
будет, код может привести к сбою JVM. [ оригинальное исследование? ]
Производительность
[ редактировать ]JNI несет значительные накладные расходы и потерю производительности при определенных обстоятельствах: [ 6 ]
- Вызовы функций методов JNI являются дорогостоящими, особенно при многократном вызове метода.
- Собственные методы не встроены в JVM, и метод не может быть скомпилирован JIT , поскольку метод уже скомпилирован.
- Массив Java можно скопировать для доступа в собственном коде, а затем скопировать обратно. Стоимость может быть линейной в зависимости от размера массива.
- Если методу передается объект или ему необходимо выполнить обратный вызов, то собственный метод, скорее всего, будет выполнять собственные вызовы JVM. Доступ к полям, методам и типам Java из нативного кода требует чего-то похожего на отражение . Сигнатуры указываются в строках и запрашиваются у JVM. Это медленно и подвержено ошибкам.
- Строки Java являются объектами, имеют длину и закодированы. Для доступа к строке или ее создания может потребоваться копирование O(n).
Альтернативы
[ редактировать ]Собственная реализация виртуальной машины Java от Microsoft ( Visual J++ ) имела аналогичный механизм для вызова собственного кода Java, называемый Raw Native Interface ( RNI ). Кроме того, у него был простой способ вызова существующего собственного кода, который сам не знал о Java, такого как (но не ограничиваясь) Windows API, называемый J/Direct . Однако после судебного разбирательства между Sun и Microsoft по поводу этой реализации Visual J++ больше не поддерживается.
RNI был менее неуклюжим в использовании, чем JNI, поскольку не требовалось никакого учета указателя среды Java. Вместо этого ко всем объектам Java можно было получить прямой доступ. Чтобы облегчить это, использовался инструмент, который генерировал файлы заголовков из классов Java. Точно так же J/Direct было проще использовать, чем использовать необходимую промежуточную собственную библиотеку и JNI.
Java Native Access (JNA) — это разработанная сообществом библиотека, которая предоставляет Java-программистам легкий доступ к собственным общим библиотекам без использования JNI. Однако для этого требуется перераспределение зависимой библиотеки jar. Компромисс заключается в том, что JNI сложнее кодировать, а JNA медленнее. [ 7 ] JNI встроен в ядро Java.
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «Обзор собственного интерфейса Java» . Руководство и спецификация для программиста собственного интерфейса Java . Проверено 27 декабря 2018 г.
- ^ «Вызов программ на языке ассемблера из Java» . Java.net. 19 октября 2006 г. Архивировано из оригинала 30 марта 2008 г. Проверено 6 октября 2007 г.
- ^ «Запуск приложений Java из программ на языке ассемблера» . Java.net. 19 октября 2006 г. Архивировано из оригинала 11 октября 2007 г. Проверено 4 октября 2007 г.
- ^ API вызова. Сан Микросистемс. https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocase.html
- ^ «Типы JNI и структуры данных» .
- ^ «java — Что делает вызовы JNI медленными? - Stack Overflow» .
- ^ Закусило, Александр. «Github также является оригинальным источником JNA. Тесты скорости тестов JNA и JNI» . Гитхаб . Гитхаб . Проверено 30 марта 2023 г.
Библиография
[ редактировать ]- Гордон, Роб (март 1998 г.). Essential Jni: собственный интерфейс Java (1-е изд.). Прентис Холл . п. 498. ИСБН 0-13-679895-0 .
- Лян, Шэн (20 июня 1999 г.). Собственный интерфейс Java (TM): Руководство программиста и спецификации (1-е изд.). Прентис Холл . п. 320. ИСБН 0-201-32577-2 .
Внешние ссылки
[ редактировать ]- Спецификация API Oracle JNI 6.0
- Собственный интерфейс Java: Руководство программиста и спецификации
- JNI в XCode от Apple
- Обработка исключений в JNI
- Java Link (современная оболочка C++17 для JNI)