Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации
Содержание | Предыдущий | Следующий

Краткий обзор проекта


 Глава 2

Эта глава сосредотачивается на главных вопросах проектирования в JNI. Большинство вопросов проектирования в этом разделе связывается с собственными методами. Проект API Вызова охватывается в  Главе 5.

Функции интерфейса JNI и Указатели

Java доступов собственного кода функции VM, вызывая функции JNI. Функции JNI доступны через указатель на интерфейс. Указатель на интерфейс является указателем на указатель. Этот указатель указывает массиву указателей, каждый из которых указывает на функцию интерфейса. Каждая функция интерфейса при предопределенном смещении в массиве.  Рисунок 2-1 иллюстрирует организацию указателя на интерфейс.

 

Предыдущий контекст описывает это изображение.

 

 Указатель на интерфейс рисунка 2-1

Интерфейс JNI организуется как таблица виртуальной функции C++ или COM-интерфейс. Преимущество для использования таблицы интерфейса, а не соединенных проводами функциональных записей, состоит в том, что пространство имен JNI становится отдельным от собственного кода. VM может легко обеспечить многократные версии таблиц функции JNI. Например, VM может поддерживать две таблицы функции JNI:

Указатель на интерфейс JNI только допустим в текущем потоке. Собственный метод, поэтому, не должен передать указатель на интерфейс от одного потока до другого. VM реализация JNI может выделить и сохранить локальные данные потока в области, на которую указывает указатель на интерфейс JNI.

Собственные методы получают указатель на интерфейс JNI как параметр. VM, как гарантируют, передаст тот же самый указатель на интерфейс к собственному методу, когда это сделает множественные вызовы собственного метода от того же самого потока Java. Однако, собственный метод можно вызвать от различных потоков Java, и поэтому может получить различные указатели на интерфейс JNI.

Компиляция, Загрузка и Соединение Собственных Методов

Начиная с Java VM является многопоточным, собственные библиотеки должны также быть скомпилированы и соединены с мультиориентированными на многопотоковое исполнение собственными компиляторами. Например, -mt флаг должен использоваться для кода C++, скомпилированного с компилятором Studio Sun. Для кода, выполненного GNU gcc компилятор, флаги -D_REENTRANT или -D_POSIX_C_SOURCE должен использоваться. Для получения дополнительной информации, пожалуйста, сошлитесь на собственную документацию компилятора.

Собственные методы загружаются System.loadLibrary метод. В следующем примере метод инициализации class загружает специфичную для платформы собственную библиотеку в который собственный метод f определяется:


package pkg;  

class Cls { 

     native double f(int i, String s); 

     static { 

         System.loadLibrary(“pkg_Cls”); 

     } 

} 

Параметр System.loadLibrary имя библиотеки, выбранное произвольно программистом. Система следует за стандартом, но специфичный для платформы, подход, чтобы преобразовать имя библиотеки к собственному имени библиотеки. Например, система Соляриса преобразовывает имя pkg_Cls к libpkg_Cls.so, в то время как система Win32 преобразовывает то же самое pkg_Cls имя к pkg_Cls.dll.

Программист может пользоваться единственной библиотекой, чтобы сохранить все собственные методы, необходимые любому числу классов, пока эти классы должны быть загружены тем же самым загрузчиком class. VM внутренне поддерживает список загруженных собственных библиотек для каждого загрузчика class. Поставщики должны выбрать собственные имена библиотеки, которые минимизируют шанс столкновений имени.

Если базовая операционная система не поддерживает динамическое подключение, все собственные методы должны быть предварительно соединены с VM. В этом случае VM завершается System.loadLibrary вызовите, фактически не загружая библиотеку.

Программист может также вызвать функцию JNI RegisterNatives() зарегистрировать собственные методы, связанные в class. RegisterNatives() функция особенно полезна со статически соединенными функциями.

Разрешение Собственных Имен методов

Динамические компоновщики разрешают записи, основанные на их именах. Собственное имя метода связывается от следующих компонентов:

VM проверяет на соответствие имени метода для методов, которые находятся в собственной библиотеке. VM сначала ищет краткое название; то есть, имя без подписи параметра. Это тогда ищет длинное имя, которое является именем с подписью параметра. Программисты должны использовать длинное имя только, когда собственный метод перегружается с другим собственным методом. Однако, это не проблема, если у собственного метода есть то же самое имя как несобственный метод. Несобственный метод (метод Java) не находится в собственной библиотеке.

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


class Cls1 { 

  int g(int i); 

  native int g(double d); 

} 

Мы приняли простую искажающую имя схему гарантировать, что все символы Unicode преобразовывают на допустимые имена функций C. Мы используем подчеркивание (“ _ ”) символ как замена для наклонной черты (“ / ”) на полностью определенные имена class. Начиная с имени или дескриптора типа никогда не начинается с числа, мы можем использовать _0, ..., _9 для escape-последовательностей, поскольку  Таблица 2-1 иллюстрирует:

 

 Табличное 2-1 Преобразование Символа Unicode
Escape-последовательность
Обозначает
_0XXXX
символ Unicode XXXX.
Отметьте, что нижний регистр используется
представлять неASCII
Символы Unicode, например,
_0abcd в противоположность
_0ABCD.
_1
символ “_”
_2
символ“;” в подписях
_3
символ“ [“ в подписях

 

И собственные методы и API интерфейса следуют за стандартным соглашением о вызовах библиотеки по данной платформе. Например, системы UNIX используют соглашение о вызовах C, в то время как системы Win32 используют __ stdcall.

Собственные Параметры Метода

Указатель на интерфейс JNI является первым параметром собственным методам. Указатель на интерфейс JNI имеет тип JNIEnv. Второй параметр отличается в зависимости от того, статичен ли собственный метод или нестатичен. Вторым параметром нестатическому собственному методу является ссылка на объект. Вторым параметром статическому собственному методу является ссылка на свой Java class.

Остающиеся параметры соответствуют регулярным параметрам метода Java. Собственный вызов метода пасует назад свой результат к вызывающей подпрограмме через возвращаемое значение.  Глава 3 описывает отображение между типами C и Java.

 Пример кода 2-1 иллюстрирует использование функции C, чтобы реализовать собственный метод f. Собственный метод f объявляется следующим образом:


package pkg;  

class Cls { 

     native double f(int i, String s); 

     ... 

} 

C функционируют с долгим скорректированным именем Java_pkg_Cls_f_ILjava_lang_String_2 реализует собственный метод f:

 

 Пример кода 2-1 Реализация Собственного Метода Используя C
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (
     JNIEnv *env,        /* interface pointer */
     jobject obj,        /* "this" pointer */
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);

     /* process the string */
     ...

     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);

     return ...
}

Отметьте, что мы всегда управляем объектами Java, используя конверт указателя на интерфейс Используя C++, можно записать немного более чистую версию кода, как показано в  Примере кода 2-2:

 

 Пример кода 2-2 Реализации Собственного Метода Используя C++

extern "C" /* specify the C calling convention */  

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( 

     JNIEnv *env,        /* interface pointer */ 

     jobject obj,        /* "this" pointer */ 

     jint i,             /* argument #1 */ 

     jstring s)          /* argument #2 */ 

{ 

     const char *str = env->GetStringUTFChars(s, 0); 

     ... 

     env->ReleaseStringUTFChars(s, str); 

     return ... 

} 

С C++ дополнительный уровень абстракции и параметр указателя на интерфейс исчезают из исходного кода. Однако, базовый механизм является точно тем же самым как с C. В C++ функции JNI определяются как встроенные функции членства, которые расширяются до их дубликатов C.

Ссылка на Объекты Java

Типы примитивов, такие как целые числа, символы, и так далее, копируются между Java и собственным кодом. Произвольные объекты Java, с другой стороны, передает ссылка. VM должен отследить все объекты, которые передали к собственному коду, так, чтобы эти объекты не были освобождены сборщиком "мусора". У собственного кода, поочередно, должен быть способ сообщить VM, что это больше не нуждается в объектах. Кроме того, сборщик "мусора" должен быть в состоянии переместить объект, упомянутый собственным кодом.

Глобальные и Локальные Ссылки

JNI делит ссылки на объект, используемые собственным кодом в две категории: локальные и глобальные ссылки. Локальные ссылки допустимы для продолжительности собственного вызова метода, и автоматически освобождаются после собственных возвратов метода. Глобальные ссылки остаются допустимыми, пока они явно не освобождаются.

Объекты передают к собственным методам как локальные ссылки. Все объекты Java, возвращенные функциями JNI, являются локальными ссылками. JNI позволяет программисту создавать глобальные ссылки из локальных ссылок. Функции JNI, которые ожидают объекты Java, принимают и глобальные и локальные ссылки. Собственный метод может возвратить локальную или глобальную ссылку на VM как его результат.

В большинстве случаев программист должен положиться на VM, чтобы освободить все локальные ссылки после собственных возвратов метода. Однако, есть времена, когда программист должен явно освободить локальную ссылку. Рассмотрите, например, следующие ситуации:

JNI позволяет программисту вручную удалять локальные ссылки в любой точке в пределах собственного метода. Чтобы гарантировать, что программисты могут вручную освободить локальные ссылки, функциям JNI не позволяют создать дополнительные локальные ссылки, за исключением ссылок, которые они возвращают как результат.

Локальные ссылки только допустимы в потоке, в котором они создаются. Собственный код не должен передать локальные ссылки от одного потока до другого.

Реализация Локальных Ссылок

Чтобы реализовать локальные ссылки, Java, VM создает реестр для каждого перехода управления от Java до собственного метода. Реестр отображает неподвижные локальные ссылки на объекты Java, и препятствует объектам быть собранным "мусор". Все объекты Java, которые передают к собственному методу (включая тех, которые возвращаются как результаты вызовов функции JNI), автоматически добавляются к реестру. Реестр удаляется после собственных возвратов метода, позволяя все его записи быть собранным "мусор".

Есть различные способы реализовать реестр, такой как использование таблицы, связанного списка, или хэш-таблицы. Хотя подсчет ссылок может использоваться, чтобы избежать дублированных записей в реестре, реализация JNI не обязана обнаружить и свернуть двойные записи.

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

Доступ к Объектам Java

JNI обеспечивает богатый набор функций средства доступа на глобальных и локальных ссылках. Это означает, что та же самая собственная реализация метода работает независимо от того, как VM представляет объекты Java внутренне. Это - решающая причина, почему JNI может поддерживаться большим разнообразием реализаций VM.

Издержки использования функций средства доступа через непрозрачные ссылки выше чем тот из прямого доступа к структурам данных C. Мы полагаем, что в большинстве случаев программисты Java используют собственные методы, чтобы выполнить нетривиальные задачи, которые омрачают издержки этого интерфейса.

Доступ к Примитивным Массивам

Эти издержки не являются приемлемыми для больших объектов Java, содержащих много примитивных типов данных, таких как целочисленные массивы и строки. (Рассмотрите собственные методы, которые используются, чтобы выполнить векторные и матричные вычисления.) Это было бы чрезвычайно неэффективно, чтобы выполнить итерации через Java, выстраивают и получают каждый элемент с вызовом функции.

Одно решение представляет понятие "прикрепления" так, чтобы собственный метод мог попросить, чтобы VM придавил содержание массива. Собственный метод тогда получает прямой указатель на элементы. У этого подхода, однако, есть две импликации:

Мы принимаем компромисс, который преодолевает обе из вышеупомянутых проблем.

Во-первых, мы обеспечиваем ряд функций, чтобы скопировать примитивные элементы массива между сегментом массива Java и собственным буфером памяти. Используйте эти функции, если собственный метод нуждается в доступе к только небольшому числу элементов в многочисленном массиве.

Во-вторых, программисты могут использовать другой набор функций, чтобы получить вниз прикрепленную версию элементов массива. Имейте в виду, что эти функции могут потребовать, чтобы Java VM выполнил выделение хранения и копирование. Копируют ли эти функции фактически массив, зависит от реализации VM, следующим образом:

Наконец, интерфейс обеспечивает функции, чтобы сообщить VM, что собственный код больше не должен получить доступ к элементам массива. Когда Вы вызываете эти функции, система или неприкрепляет массив, или это согласовывает исходный массив со своей неподвижной копией и освобождает копию.

Наш подход обеспечивает гибкость. Алгоритм сборщика "мусора" может принять отдельные решения относительно копирования или прикрепления для каждого данного массива. Например, сборщик "мусора" может скопировать маленькие объекты, но прикрепить большие объекты.

Реализация JNI должна гарантировать, что собственные методы, работающие в многократных потоках, могут одновременно получить доступ к тому же самому массиву. Например, JNI может сохранить внутренний счетчик для каждого прикрепленного массива так, чтобы один поток не неприкрепил массив, который также прикрепляется другим потоком. Отметьте, что JNI не должен заблокировать примитивные массивы для эксклюзивного доступа собственным методом. Одновременно обновление массива Java от различных потоков приводит к недетерминированным результатам.

Доступ к Полям и Методам

JNI позволяет собственному коду получать доступ к полям и вызывать методы объектов Java. JNI идентифицирует методы и поля их символьными именами и подписями типа. Двухступенчатый процесс факторизует стоимость определения местоположения поля или метода с его имени и подписи. Например, чтобы вызвать метод f в class cls, собственный код сначала получает ID метода, следующим образом:


jmethodID mid =      env->GetMethodID(cls, “f”, “(ILjava/lang/String;)D”); 

Собственный код может тогда неоднократно использовать ID метода без стоимости поиска метода, следующим образом:


jdouble result = env->CallDoubleMethod(obj, mid, 10, str); 

ID поля или метода не препятствует тому, чтобы VM разгрузил class, из которого был получен ID. После того, как class разгружается, ID метода или поля становится недопустимым. Собственный код, поэтому, должен удостовериться к:

если это намеревается использовать метод или полевой ID в течение длительного периода времени.

JNI не вводит ограничений для того, как поле и ID метода реализуются внутренне.

Создание отчетов об Ошибках Программирования

JNI не проверяет на программирование ошибок, таких как передача в Нулевых указателях или недопустимых типах параметра. Недопустимые типы параметра включают такие вещи как использование нормального объекта Java вместо Java объект class. JNI не проверяет на эти ошибки программирования по следующим причинам:

Большинство библиотечных функций C не принимает меры против программирования ошибок. printf() функция, например, обычно вызывает ошибку периода выполнения, вместо того, чтобы возвратить код ошибки, когда она получает недопустимый адрес. Принуждение C библиотечные функции, чтобы проверить на все возможные состояния ошибки, вероятно, привело бы к таким проверкам, которые будут дублированы - однажды в пользовательском коде, и с другой стороны в библиотеке.

Программист не должен передать недопустимые указатели или параметры неправильного типа к функциям JNI. Выполнение так могло привести к произвольным последствиям, включая поврежденное системное состояние или катастрофический отказ VM.

Исключения Java

JNI позволяет собственным методам повышать произвольные исключения Java. Собственный код может также обработать выдающиеся исключения Java. Исключения Java, оставленные необработанный, распространяются назад к VM.

Исключения и Коды ошибки

Определенные функции JNI используют механизм исключения Java, чтобы сообщить о состояниях ошибки. В большинстве случаев функции JNI сообщают о состояниях ошибки, возвращая код ошибки и выдавая исключение Java. Код ошибки обычно является специальным возвращаемым значением (таким как НУЛЬ), который является за пределами диапазона нормальных возвращаемых значений. Поэтому, программист может:

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

Во всех других случаях неошибочное возвращаемое значение гарантирует, что никакие исключения не были выданы.

Асинхронные Исключения

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

Отметьте, что только те функция JNI, которая могла потенциально повысить синхронную проверку исключений на асинхронные исключения.

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

Обработка исключений

Есть два способа обработать исключение в собственном коде:

После того, как исключение было повышено, собственный код должен сначала очистить исключение прежде, чем выполнить другие вызовы JNI. Когда есть исключение на ожидании, функции JNI, которые безопасно вызвать:


  ExceptionOccurred()
  ExceptionDescribe()
  ExceptionClear()
  ExceptionCheck()
  ReleaseStringChars()
  ReleaseStringUTFChars()
  ReleaseStringCritical()
  Release<Type>ArrayElements()
  ReleasePrimitiveArrayCritical()
  DeleteLocalRef()
  DeleteGlobalRef()
  DeleteWeakGlobalRef()
  MonitorExit()
  PushLocalFrame()
  PopLocalFrame()

 

 


Содержание | Предыдущий | Следующий

Oracle и/или его филиалы Авторское право © 1993, 2012, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами