Что такое нативная реализация в Java?

Если мы посмотрим на класс Java Object, то мы можем найти некоторые из методов, таких как:

public native int hashCode()
protected native Object clone()

Что это за аборигены и как эти методы работают?

5 ответов

Решение

Эти методы являются либо встроенными, либо написаны вне Java в "нативном" коде, то есть для конкретной машины.

Те, что вы упоминаете, являются встроенными и являются частью JDK, но вы также можете написать собственные методы, используя собственный интерфейс Java (JNI). Обычно для написания методов используется C, но многие другие языки, такие как python, позволяют довольно легко писать методы таким способом. Код написан таким образом либо для повышения производительности, либо потому, что ему необходим доступ к инфраструктуре, специфичной для платформы, что невозможно сделать на обычном Java.

В случае hashcode()это реализуется JVM. Это потому, что часто хеш-код будет связан с чем-то, что знает только JVM. В ранних JVM это было связано с расположением объекта в памяти - в других JVM объект может перемещаться в памяти, и поэтому может использоваться более сложная (но все же очень быстрая) схема.

Большинство нативных методов реализованы с использованием JNI, как указано в других ответах.

Тем не менее, критичные к производительности методы, такие как Object.hashCode обычно реализуются как встроенные. Когда байт-код компилируется в машинный код, компилятор Java распознает вызов метода и напрямую вставляет соответствующий код. Это, очевидно, будет намного быстрее, чем пройти через JNI для тривиального метода.

Многие люди будут утверждать, что Object.hashCode вернет адрес представления объекта в памяти. В современных реализациях объекты фактически перемещаются в памяти. Вместо этого область заголовка объекта используется для хранения значения, которое может быть лениво извлечено из адреса памяти во время первого запроса значения.

Нативные методы реализованы в основном в C и скомпилированы в нативный код, который выполняется непосредственно на машине. Это отличается от обычных методов, которые реализованы в Java и скомпилированы в байт-код Java, который выполняется виртуальной машиной Java (JVM).

Для взаимодействия с этими методами из Java вам нужно использовать собственный интерфейс Java (JNI).

Нативный код в основном необходим для доступа к низкоуровневым материалам. В случае hashCode это адрес объекта в памяти. Я предполагаю, что для клона он копирует необработанную память из объекта отдачи в клонированный. Другие варианты использования собственного кода предназначены для доступа к функциям или оборудованию ОС.

Недостаток использования нативного кода заключается в том, что вы теряете безопасность JVM, то есть ваша программа может аварийно завершить работу или иметь дыры в безопасности из-за ошибок в нативном коде.

Что это за аборигены и как эти методы работают?

Минимальный пример, чтобы прояснить ситуацию:

Main.java:

public class Main {
    public native int square(int i);
    public static void main(String[] args) {
        System.loadLibrary("Main");
        System.out.println(new Main().square(2));
    }
}

Main.c:

#include <jni.h>
#include "Main.h"

JNIEXPORT jint JNICALL Java_Main_square(
    JNIEnv *env, jobject obj, jint i) {
  return i * i;
}

Скомпилируйте и запустите:

sudo apt-get install build-essential openjdk-7-jdk
export JAVA_HOME='/usr/lib/jvm/java-7-openjdk-amd64'
javac Main.java
javah -jni Main
gcc -shared -fpic -o libMain.so -I${JAVA_HOME}/include \
  -I${JAVA_HOME}/include/linux Main.c
java -Djava.library.path=. Main

Выход:

4

Проверено на Ubuntu 14.04. Также работал с Oracle JDK 1.8.0_45.

Пример на GitHub для вас, чтобы играть с.

Интерпретация:

Это позволяет вам:

  • вызвать скомпилированную динамически загружаемую библиотеку (здесь написанную на C) с произвольным ассемблерным кодом из Java
  • и получить результаты обратно в Java

Это может быть использовано для:

  • пишите более быстрый код в критической секции с лучшими инструкциями по сборке процессора (не переносим процессор)
  • делать прямые системные вызовы (не переносимые ОС)

с компромиссом более низкой мобильности.

Вы также можете вызывать Java из C, но вы должны сначала создать JVM в C: Как вызывать функции Java из C++?

Пример в OpenJDK 8

Давайте найдем где Object#clone определяется в jdk8u60-b27.

Сначала мы находим:

find . -name Object.java

что приводит нас к jdk / src / share / classes / java / lang / Object.java # l212:

protected native Object clone() throws CloneNotSupportedException;

Теперь самое сложное - найти место, где клон находится среди всего косвенного. Вопрос, который мне помог, был:

find . -iname object.c

который найдет файлы C или C++, которые могут реализовать нативные методы Object. Это приводит нас к jdk/share/native/java/lang/Object.C# l47:

static JNINativeMethod methods[] = {
    ...
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

что приводит нас к JVM_Clone условное обозначение:

grep -R JVM_Clone

что приводит нас к hotspot / src / share / vm / prims / jvm.cpp # l580:

JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
    JVMWrapper("JVM_Clone");

Развернув кучу макросов, мы приходим к выводу, что это точка определения.

Собственные методы в Java реализованы с использованием " Собственного интерфейса Java", известного как JNI.

Другие вопросы по тегам