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

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

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Чтобы взаимодействовать с этими методами с Java, вам необходимо использовать Java Native Interface (JNI).

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

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

Ответ 4

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

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

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 из С++?

Пример в OpenJDK 8

Найдите find, где 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 или С++, которые могли бы использовать собственные методы Object. Это приводит нас к jdk/share/native/java/lang/Object.С# 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");

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

Ответ 5

Нативные методы в Java реализованы с использованием Java Native Interface, известного как JNI.