Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From cozy Java to brutal Native code or There and Back Again (ITeaConf 2021, RU)

Ivan Ugliansky
November 14, 2021
39

From cozy Java to brutal Native code or There and Back Again (ITeaConf 2021, RU)

Версия доклада, где меньше внимания уделяется фреймворкам JNA, JNR и JavaCPP, но зато обновлена информация и примеры про Panama в контексте Java 17.

---

Все мы любим Java и другие управляемые языки, но иногда их бывает просто недостаточно. Нужен доступ к библиотеке машинного обучения, написанной исключительно на С? Или всего-то хочется позвать небольшой метод из OS API? А может, для одного из модулей вы хотите получить большую производительность, чем вы когда-либо сможете выжать из Java? Тогда вам прямой путь в нативный код!

Вроде все просто — пишем нативный метод, загружаем библиотеку, вызываем, и дело в шляпе! Но вот беда: JVM начала крашиться в случайных местах, производительность упала ниже прежнего, а ваш репозиторий заполонили бесконечные сишные файлы, испещренные буквами J-N-I. Что же пошло не так?

В докладе разберемся со всем по порядку: что необычного в интеропе между Java и нативным кодом и какая у него история; как работать с нативным кодом прямо сейчас, чтобы избежать сегфолтов, просадки производительности, внезапных OOM; наконец, что же нас ждет в будущем в контексте таких проектов, как Panama и Sulong, и будет ли после них вообще смысл говорить про какой-то интероп.

Ivan Ugliansky

November 14, 2021
Tweet

Transcript

  1. Какой еще нативный код? 4 public class JavaToNative { static

    native void goNative(); static native void goThere(Callback andBackAgain); }
  2. Какой еще нативный код? 5 public class JavaToNative { static

    native void goNative(); static native void goThere(Callback andBackAgain); }
  3. Какой еще нативный код? 6 public class JavaToNative { static

    native void goNative(); static native void goThere(Callback andBackAgain); } Java C/C++/... goNative
  4. Какой еще нативный код? 7 public class JavaToNative { static

    native void goNative(); static native void goThere(Callback andBackAgain); } Java goThere andBackAgain C/C++/...
  5. Но зачем нам нативы? 10 Java - managed язык ◦

    автоматическое управление памятью
  6. Но зачем нам нативы? 11 Java - managed язык ◦

    автоматическое управление памятью ◦ безопасные операции
  7. Но зачем нам нативы? 12 Java - managed язык ◦

    автоматическое управление памятью ◦ безопасные операции Java - это...
  8. 13

  9. Но зачем нам нативы? 14 Java - managed язык ◦

    автоматическое управление памятью ◦ безопасные операции А вот нативный код - это...
  10. Причины звать нативный код из Java 18 1. Есть отличная

    библиотека! (Но она на С/C++) OpenGL, DirectX, Tensorflow, Cuda, OpenCL, OpenSSl, Vulkan, Криптография, ...
  11. Причины звать нативный код из Java 19 1. Есть отличная

    библиотека! (Но она на С/C++) 2. Да мне всего лишь один метод из WinAPI позвать!
  12. Причины звать нативный код из Java 20 1. Есть отличная

    библиотека! (Но она на С/C++) 2. Да мне всего лишь один метод из WinAPI позвать! 3. Напишу один модуль на C++, все как разгонится!
  13. 21

  14. 22 A fatal error has been detected by the Java

    Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007f542922359f, pid=25670, tid=0x00007f5444efa700 # # JRE version: Java(TM) SE Runtime Environment (8.0_151-b12) (build 1.8.0_151-b12) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libavutil.so.56+0x1159f] av_strstart+0x1f
  15. 23 A fatal error has been detected by the Java

    Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007f542922359f, pid=25670, tid=0x00007f5444efa700 # # JRE version: Java(TM) SE Runtime Environment (8.0_151-b12) (build 1.8.0_151-b12) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libavutil.so.56+0x1159f] av_strstart+0x1f # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000180005b00, pid=13432, tid=13952 # # JRE version: Java(TM) SE Runtime Environment (12.0.2+10) (build 12.0.2+10) # Java VM: Java HotSpot(TM) 64-Bit Server VM (12.0.2+10, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64) # Problematic frame: # C [rxtxSerial.dll+0x5b00]
  16. 24 A fatal error has been detected by the Java

    Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007f542922359f, pid=25670, tid=0x00007f5444efa700 # # JRE version: Java(TM) SE Runtime Environment (8.0_151-b12) (build 1.8.0_151-b12) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libavutil.so.56+0x1159f] av_strstart+0x1f # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000180005b00, pid=13432, tid=13952 # # JRE version: Java(TM) SE Runtime Environment (12.0.2+10) (build 12.0.2+10) # Java VM: Java HotSpot(TM) 64-Bit Server VM (12.0.2+10, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64) # Problematic frame: # C [rxtxSerial.dll+0x5b00] # SIGSEGV (0xb) at pc=0x00007f098dac2618, pid=1720, tid=0x00007f0963f7a700 # # JRE version: OpenJDK Runtime Environment (8.0_212-b03) (build 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03) # Java VM: OpenJDK 64-Bit Server VM (25.212-b03 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libopencv_core.so.3.2+0x132618] cv::_InputArray::size(int) const+0x1d8
  17. 25 A fatal error has been detected by the Java

    Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007f542922359f, pid=25670, tid=0x00007f5444efa700 # # JRE version: Java(TM) SE Runtime Environment (8.0_151-b12) (build 1.8.0_151-b12) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libavutil.so.56+0x1159f] av_strstart+0x1f # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000180005b00, pid=13432, tid=13952 # # JRE version: Java(TM) SE Runtime Environment (12.0.2+10) (build 12.0.2+10) # Java VM: Java HotSpot(TM) 64-Bit Server VM (12.0.2+10, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64) # Problematic frame: # C [rxtxSerial.dll+0x5b00] # SIGSEGV (0xb) at pc=0x00007f098dac2618, pid=1720, tid=0x00007f0963f7a700 # # JRE version: OpenJDK Runtime Environment (8.0_212-b03) (build 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03) # Java VM: OpenJDK 64-Bit Server VM (25.212-b03 mixed mode linux-amd64 compressed oops) # Problematic frame: # C [libopencv_core.so.3.2+0x132618] cv::_InputArray::size(int) const+0x1d8
  18. Будем разбираться 28 1. Почему так много проблем с нативами?

    2. Как пройти в Мордор и не получить SIGSEGV?
  19. Как позвать натив? 32 Как себя должен вести GC? Где

    взять нативы? Как работать с Java из натива?
  20. 1. JDK 1.0 NMI — Native Method Invocation (Sun JVM)

    2. Raw Native Interface (RNI) in Microsoft J++ and J/Direct 3. Netscape’s JRI (Java Runtime Interface) 4. ... История до нашей эры 34
  21. Наша эра 36 JNI - Java Native Interface ✓ Единый

    интерфейс, чтобы править всеми!
  22. Наша эра 37 JNI - Java Native Interface ✓ Единый

    интерфейс, чтобы править всеми! ✓ Детали реализации скрыты ⇒ JVM нейтрален, GC нейтрален
  23. Как это выглядит со стороны Java? 39 public class JavaToNative

    { static native void goNative(); static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } }
  24. Как это выглядит со стороны Java? 40 public class JavaToNative

    { static native void goNative(); static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } }
  25. Как это выглядит со стороны Java? 41 public class JavaToNative

    { static native void goNative(); static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } }
  26. Как это выглядит со стороны Java? 42 public class JavaToNative

    { static native void goNative(Object obj); static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } } class Callback { private final String transport; public Callback(String transport) { this.transport = transport; } public void call() { System.out.println("Ok, we are in Shire again! Returned by " + transport); } }
  27. Как это выглядит со стороны С? 44 public class JavaToNative

    { static native void goThere(Callback andBackAgain); }
  28. Как это выглядит со стороны С? 45 public class JavaToNative

    { static native void goThere(Callback andBackAgain); } javac JavaToNative.java -h .
  29. Как это выглядит со стороны С? 46 public class JavaToNative

    { static native void goThere(Callback andBackAgain); } JNIEXPORT void JNICALL Java_JavaToNative_goThere (JNIEnv *, jclass, jobject); javac JavaToNative.java -h . JavaToNative.h
  30. Как это выглядит со стороны С? 47 public class JavaToNative

    { static native void goThere(Callback andBackAgain); } JNIEXPORT void JNICALL Java_JavaToNative_goThere (JNIEnv *, jclass, jobject); javac JavaToNative.java -h . JavaToNative.h
  31. Как это выглядит со стороны С? 48 boolean jboolean unsigned

    8 bits byte jbyte signed 8 bits char jchar unsigned 16 bits short jshort signed 16 bits int jint signed 32 bits long jlong signed 64 bits float jfloat 32 bits double jdouble 64 bits void void N/A Примитивные
  32. Как это выглядит со стороны С? 49 boolean jboolean unsigned

    8 bits byte jbyte signed 8 bits char jchar unsigned 16 bits short jshort signed 16 bits int jint signed 32 bits long jlong signed 64 bits float jfloat 32 bits double jdouble 64 bits void void N/A Примитивные Ссылочные jobject jclass jstring jthrowable jarray jobjectArray jbooleanArray jcharArray ...
  33. Как это выглядит со стороны С? 50 public class JavaToNative

    { static native void goThere(Callback andBackAgain); } JNIEXPORT void JNICALL Java_JavaToNative_goThere (JNIEnv *, jclass, jobject); javac JavaToNative.java -h . JavaToNative.h
  34. Как это выглядит со стороны С? 53 JNIEnv ✓ Указатель

    на JNINativeInterface (214 функций) GetVersion, DefineClass, FindClass, FromReflectedMethod, FromReflectedField, ToReflectedMethod, GetSuperclass, IsAssignableFrom, ToReflectedField, Throw, ThrowNew, ExceptionOccurred, ExceptionDescribe, ExceptionClear, FatalError, PushLocalFrame, PopLocalFrame, NewGlobalRef, DeleteGlobalRef, DeleteLocalRef, IsSameObject, NewLocalRef, EnsureLocalCapacity, AllocObject, NewObject, NewObjectV, NewObjectA, GetObjectClass, IsInstanceOf, GetMethodID, CallObjectMethod, CallObjectMethodV, CallObjectMethodA, CallBooleanMethod, CallBooleanMethodV, CallBooleanMethodA, CallByteMethod, CallByteMethodV, CallByteMethodA, CallCharMethod, CallCharMethodV, CallCharMethodA, CallShortMethod, CallShortMethodV, CallShortMethodA, CallIntMethod, CallIntMethodV, CallIntMethodA, CallLongMethod, CallLongMethodV, CallLongMethodA, CallFloatMethod, CallFloatMethodV, CallFloatMethodA, CallDoubleMethod, CallDoubleMethodV, CallDoubleMethodA, CallVoidMethod, CallVoidMethodV, CallVoidMethodA, CallNonvirtualObjectMethod, CallNonvirtualObjectMethodV, CallNonvirtualObjectMethodA, CallNonvirtualBooleanMethod, CallNonvirtualBooleanMethodV, CallNonvirtualBooleanMethodA, CallNonvirtualByteMethod, CallNonvirtualByteMethodV, CallNonvirtualByteMethodA, CallNonvirtualCharMethod, CallNonvirtualCharMethodV, CallNonvirtualCharMethodA, CallNonvirtualShortMethod, CallNonvirtualShortMethodV, CallNonvirtualShortMethodA, CallNonvirtualIntMethod, CallNonvirtualIntMethodV, CallNonvirtualIntMethodA, CallNonvirtualLongMethod, CallNonvirtualLongMethodV, CallNonvirtualLongMethodA, CallNonvirtualFloatMethod, CallNonvirtualFloatMethodV, CallNonvirtualFloatMethodA, CallNonvirtualDoubleMethod, CallNonvirtualDoubleMethodV, CallNonvirtualDoubleMethodA, CallNonvirtualVoidMethod, CallNonvirtualVoidMethodV, CallNonvirtualVoidMethodA, GetFieldID, GetObjectField, GetBooleanField, GetByteField, GetCharField, GetShortField, GetIntField, GetLongField, GetFloatField, GetDoubleField, SetObjectField, SetBooleanField, SetByteField, SetCharField, SetShortField, SetIntField, SetLongField, SetFloatField, SetDoubleField, GetStaticMethodID, CallStaticObjectMethod, CallStaticObjectMethodV, CallStaticObjectMethodA, CallStaticBooleanMethod, CallStaticBooleanMethodV, CallStaticBooleanMethodA, CallStaticByteMethod, CallStaticByteMethodV, CallStaticByteMethodA, CallStaticCharMethod, CallStaticCharMethodV, CallStaticCharMethodA, CallStaticShortMethod, CallStaticShortMethodV, CallStaticShortMethodA, CallStaticIntMethod, CallStaticIntMethodV, CallStaticIntMethodA, CallStaticLongMethod, CallStaticLongMethodV, CallStaticLongMethodA, CallStaticFloatMethod, CallStaticFloatMethodV, CallStaticFloatMethodA, CallStaticDoubleMethod, CallStaticDoubleMethodV, CallStaticDoubleMethodA, CallStaticVoidMethod, CallStaticVoidMethodV, CallStaticVoidMethodA, GetStaticFieldID, GetStaticObjectField, GetStaticBooleanField, ...
  35. Как это выглядит со стороны С? 54 JNIEnv ✓ Указатель

    на JNINativeInterface (214 функций) NewObject, GetObjectClass, GetObjectField, SetObjectField, Get<PrimType>Field, Set<PrimType>Field, CallObjectMethod, CallStaticObjectMethod Call<PrimType>Method, CallStatic<PrimType>Method Throw, ThrowNew, Очень похоже на Reflection! «мета»-программирование на Java
  36. Как это выглядит со стороны С? 55 JNIEnv ✓ Указатель

    на JNINativeInterface (214 функций) ✓ Только через них можно взаимодействовать с Java docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
  37. Как это выглядит со стороны С? 56 /* * Class:

    JavaToNative * Method: goThere * Signature: (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { } JavaToNative.c
  38. Как это выглядит со стороны С? 57 /* * Class:

    JavaToNative * Method: goThere * Signature: (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { printf("Ok, we are in Mordor now!\n"); } JavaToNative.c
  39. Как это выглядит со стороны С? 58 /* * Class:

    JavaToNative * Method: goThere * Signature: (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); } JavaToNative.c
  40. Как это выглядит со стороны С? 59 /* * Class:

    JavaToNative * Method: goThere * Signature: (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, andBackAgain, method); } JavaToNative.c
  41. Собираем! 62 gcc.exe -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I %JAVA_HOME%/include -I %JAVA_HOME%/include/win32

    -I. -L %JAVA_HOME%/jre/lib -shared JavaToNative.c -o lib/NativeLib.dll Nokee plugins ✓ Кроссплатформенное решение ✓ Удобное использование через Gradle ✓ nokee.dev
  42. Собираем и запускаем! 63 gcc.exe -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I %JAVA_HOME%/include

    -I %JAVA_HOME%/include/win32 -I. -L %JAVA_HOME%/jre/lib -shared JavaToNative.c -o lib/NativeLib.dll public class JavaToNative { static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } }
  43. Собираем и запускаем! 64 gcc.exe -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I %JAVA_HOME%/include

    -I %JAVA_HOME%/include/win32 -I. -L %JAVA_HOME%/jre/lib -shared JavaToNative.c -o lib/NativeLib.dll public class JavaToNative { static native void goThere(Callback andBackAgain); public static void main(String[] args) { System.loadLibrary("NativeLib"); goThere(new Callback("Eagles")); } } $ java -Djava.library.path=./lib JavaToNative Ok, we are in Mordor now! Ok, we are in Shire again! Returned by Eagles
  44. 67 /* * Class: JavaToNative * Method: goThere * Signature:

    (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, andBackAgain, method); }
  45. 68 /* * Class: JavaToNative * Method: goThere * Signature:

    (LCallback;Ljava/lang/Object)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain, jobject luggage) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, luggage, method); } java -Djava.library.path=./lib JavaToNative
  46. 69 /* * Class: JavaToNative * Method: goThere * Signature:

    (LCallback;Ljava/lang/Object)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain, jobject luggage) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, luggage, method); } java -Djava.library.path=./lib JavaToNative Ok, we are in Mordor now! # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffb8add70e1, pid=14100, tid=12564 # # JRE version: OpenJDK Runtime Environment AdoptOpenJDK (14.0.1+7) (build 14.0.1+7) # Java VM: OpenJDK 64-Bit Server VM AdoptOpenJDK (14.0.1+7, mixed mode, sharing, tiered, compressed oops , g1 gc, windows-amd64) # Problematic frame: # V [jvm.dll+0x3970e1]
  47. Что может пойти не так? 70 ✓ Статическая типовая информация

    утеряна. Добро пожаловать в JavaScript! ✓ Вызов правильных JNI функций - ваша ответственность
  48. Что может пойти не так? 71 ✓ Статическая типовая информация

    утеряна. Добро пожаловать в JavaScript! ✓ Вызов правильных JNI функций - ваша ответственность ✓ Исключения из Java не пробрасываются (см. ExceptionOccurred, ExceptionClear)
  49. 73 /* * Class: JavaToNative * Method: goThere * Signature:

    (LCallback;Ljava/lang/Object)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain, jobject luggage) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, luggage, method); } java -Djava.library.path=./lib JavaToNative
  50. 74 /* * Class: JavaToNative * Method: goThere * Signature:

    (LCallback;Ljava/lang/Object)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain, jobject luggage) { printf("Ok, we are in Mordor now!\n"); jclass cls = (*env)->GetObjectClass(env, andBackAgain); jmethodID method = (*env)->GetMethodID(env, cls, "call", "()V"); (*env)->CallVoidMethod(env, luggage, method); } java -Xcheck:jni -Djava.library.path=./lib JavaToNative
  51. 75 java -Xcheck:jni -Djava.library.path=./lib JavaToNative Ok, we are in Mordor

    now! FATAL ERROR in native method: Wrong object class or methodID passed to JNI call at JavaToNative.goThere(Native Method) at JavaToNative.main(JavaToNative.java:9)
  52. Что может пойти не так? 76 ✓ Статическая типовая информация

    утеряна. Добро пожаловать в JavaScript! ✓ Вызов правильных JNI функций - ваша ответственность ✓ Исключения из Java не пробрасываются ✓ -Xcheck:jni значительно улучшает диагностику простых случаев (но это стоит дорого)
  53. Как позвать натив? 77 Как себя должен вести GC? Где

    взять нативы? Как работать с Java из натива?
  54. 79 ◦ safe-points, в которых "припарковываются" потоки ◦ GC ждет,

    пока потоки припаркуются Threads in Java code GC request GC в Java коде
  55. GC в Java коде 80 ◦ safe-points, в которых "припарковываются"

    потоки ◦ GC ждет, пока потоки припаркуются Threads in Java code GC request
  56. GC в Java коде 81 ◦ safe-points, в которых "припарковываются"

    потоки ◦ GC ждет, пока потоки припаркуются Threads in Java code GC Threads GC request
  57. GC в нативном коде 82 ◦ какие еще safe-points? ◦

    натив продолжает работать
  58. 83 Threads in Java code GC Threads GC request GC

    в нативном коде ◦ какие еще safe-points? ◦ натив продолжает работать
  59. 84 GC в нативном коде ◦ какие еще safe-points? ◦

    натив продолжает работать Threads in Java code GC Threads GC request Threads in native
  60. 85 ◦ какие еще safe-points? ◦ натив продолжает работать ◦

    на входе и выходе из натива синхронизация с GC Threads in Java code GC Threads GC request Threads in native GC в нативном коде
  61. 87 public class JavaToNative { static native void goThere(Callback andBackAgain);

    } JNIEXPORT void JNICALL Java_JavaToNative_goThere (JNIEnv *, jclass, jobject); javac JavaToNative.java -h . JavaToNative.h GC в нативном коде
  62. 88 jobject и компания - специальные хендлы, для которых: 1.

    JVM поддерживает связь с реальными Java объектами j.l.Object jobject Native Heap Java Heap GC в нативном коде
  63. 89 jobject и компания - специальные хендлы, для которых: 1.

    JVM поддерживает связь с реальными Java объектами j.l.Object jobject Native Heap Java Heap GC в нативном коде
  64. 90 jobject и компания - специальные хендлы, для которых: 1.

    JVM поддерживает связь с реальными Java объектами 2. Доступ к реальным Java объектам (через JNI) синхронизирован с GC j.l.Object jobject Native Heap Java Heap GC в нативном коде
  65. 91 jobject и компания - специальные хендлы, для которых: 1.

    JVM поддерживает связь с реальными Java объектами 2. Доступ к реальным Java объектам (через JNI) синхронизирован с GC 3. Содержимое считается GC-roots! j.l.Object jobject Native Heap Java Heap GC в нативном коде
  66. Подводные камни (GC + Natives) 93 ✓ Для хендлов реализована

    альтернативная система управления памятью
  67. Подводные камни (GC + Natives) 94 ✓ Для хендлов реализована

    альтернативная система управления памятью Типы хендлов (в коде все выглядит, как jobject & Co):
  68. Подводные камни (GC + Natives) 95 ✓ Для хендлов реализована

    альтернативная система управления памятью Типы хендлов (в коде все выглядит, как jobject & Co): 1. Local Reference живут не дольше одного нативного вызова
  69. Подводные камни (GC + Natives) 96 ✓ Для хендлов реализована

    альтернативная система управления памятью Типы хендлов (в коде все выглядит, как jobject & Co): 1. Local Reference 2. Global Reference 3. Weak Global Reference живут не дольше одного нативного вызова живут, пока их явно не освободят живут, пока их явно не освободят, но GC может собрать Java объект
  70. Подводные камни (GC + Natives) 97 ✓ Особая система управления

    памятью (в которой легко получить memory leak/dangling pointer)
  71. Подводные камни (GC + Natives) 98 ✓ Особая система управления

    памятью (в которой легко получить memory leak/dangling pointer) ✓ Особая обработка массивов и строк: pinning vs copying
  72. Подводные камни (GC + Natives) 99 ✓ Особая система управления

    памятью (в которой легко получить memory leak/dangling pointer) ✓ Особая обработка массивов и строк: pinning vs copying ✓ Опасные JNI методы Get*Critical (могут сильно помешать работе GC вплоть до зависания JVM) shipilev.net/jvm/anatomy-quarks/9-jni-critical-gclocker
  73. Производительность нативных вызовов 104 прямой вызов Java метода без инлайна

    вызов нативного метода (без параметров) из Java Java Java Java Native OpenJDK 17.0.1
  74. Производительность нативных вызовов 105 прямой вызов Java метода без инлайна

    вызов нативного метода (без параметров) из Java Разница в 6 раз На jdk-11.0.7 Java Java Java Native OpenJDK 17.0.1
  75. Производительность нативных вызовов 106 вызов нативного метода (без параметров) из

    Java Вызов нативного метода (без параметров) из Java, а из него вызов Java метода (без параметров) Java Native Java Java Native OpenJDK 17.0.1
  76. вызов нативного метода (без параметров) из Java Вызов нативного метода

    (без параметров) из Java, а из него вызов Java метода (без параметров) Разница в 5 раз Производительность нативных вызовов 107 Java Native Java Java Native OpenJDK 17.0.1
  77. 110 static native void goNative(); mov DWORD PTR [rsp-0x14000],eax push

    rbp mov rbp,rsp sub rsp,0x40 movabs r14,0x76d381c90; mov QWORD PTR [rsp+0x30],r14 lea r14,[rsp+0x30] mov rsi,r14 movabs r10,0x7f486d4481fe mov QWORD PTR [r15+0x1c8],r10 mov QWORD PTR [r15+0x1c0],rsp cmp BYTE PTR [rip+0x154b0527],0x0 je 0x00007f486d448255 push rsi movabs rsi,0x7f486b1433f8 mov rdi,r15 test esp,0xf je 0x00007f486d44824f sub rsp,0x8 call 0x00007f4882317be0 add rsp,0x8 jmp 0x00007f486d448254 call 0x00007f4882317be0 pop rsi lea rdi,[r15+0x1e0] mov DWORD PTR [r15+0x258],0x4 call 0x00007f486073a79a vzeroupper mov DWORD PTR [r15+0x258],0x5 mov ecx,r15d shr ecx,0x4 and ecx,0xffc movabs r10,0x7f4883580000 mov DWORD PTR [r10+rcx*1],ecx cmp DWORD PTR [rip+0x154bbf5e],0x0 jne 0x00007f486d4482b2 cmp DWORD PTR [r15+0x30],0x0 je 0x00007f486d4482cb call 0x00007f4882317be0 add rsp,0x8 jmp 0x00007f486d448254 call 0x00007f4882317be0 pop rsi lea rdi,[r15+0x1e0] mov DWORD PTR [r15+0x258],0x4 call 0x00007f486073a79a mov DWORD PTR [r15+0x258],0x4 call 0x00007f486073a79a vzeroupper mov DWORD PTR [r15+0x258],0x5 mov ecx,r15d shr ecx,0x4 and ecx,0xffc movabs r10,0x7f4883580000 mov DWORD PTR [r10+rcx*1],ecx cmp DWORD PTR [rip+0x154bbf5e],0x0 jne 0x00007f486d4482b2 cmp DWORD PTR [r15+0x30],0x0 je 0x00007f486d4482cb mov rdi,r15 mov r12,rsp sub rsp,0x0 and rsp,0xfffffffffffffff0 call 0x00007f48823b84f0 mov rsp,r12 xor r12,r12 mov DWORD PTR [r15+0x258],0x8 cmp DWORD PTR [r15+0x284],0x1 je 0x00007f486d448369 cmp BYTE PTR [rip+0x154b0456],0x0 je 0x00007f486d448324 movabs rsi,0x7f486b1433f8 mov rdi,r15 test esp,0xf je 0x00007f486d44831f sub rsp,0x8 call 0x00007f4882317c70 add rsp,0x8 jmp 0x00007f486d448324 call 0x00007f4882317c70 movabs r10,0x0 mov QWORD PTR [r15+0x1c0],r10 movabs r10,0x0 mov QWORD PTR [r15+0x1c8],r10 mov rcx,QWORD PTR [r15+0x38] mov DWORD PTR [rcx+0x100],0x0 leave cmp QWORD PTR [r15+0x8],0x0 jne 0x00007f486d448364 ret jmp Stub::forward exception
  78. Производительность нативных вызовов 111 ✓ State transition (Java -> Native

    и Native -> Java) очень дорог: 1. Синхронизация с GC 2. Завертка параметров в Local References 3. Обработка результата + exception check 4. Перепушивание параметров
  79. Производительность нативных вызовов 112 ✓ State transition (Java -> Native

    и Native -> Java) очень дорог: 1. Синхронизация с GC 2. Завертка параметров в Local References 3. Обработка результата + exception check 4. Перепушивание параметров ✓ Никакого инлайнинга! ✓ Особенности реализации Hotspot (стабы для перехода Native -> Java)
  80. 1. javac -h для генерации .h файлов 2. nokee.dev для

    сборки 3. -Xcheck:jni для отлавливания ошибок 4. Осторожнее с JNI References и с JNI Get*Critical 5. Переход в натив (и возврат в Java) очень дорогой 113 Takeaways про JNI
  81. Идея: ✓ Весь код писать на Java, а связь с

    нативом генерировать автоматически 116 Новейшее время
  82. Идея: ✓ Весь код писать на Java, а связь с

    нативом генерировать автоматически Реализация: ✓ Библиотеки: JNA, JNR, JavaCPP, ... 117 Новейшее время
  83. 120 JNA __declspec(dllexport) void __stdcall sayHello(const char* name) { printf("Hello

    %s from native!\n", name); } MyNativeLib.c public interface MyNativeLibrary extends Library { MyNativeLibrary INSTANCE = (MyNativeLibrary) Native.load("MyNativeLib", MyNativeLibrary.class); void sayHello(String name); } TestJNA.java MyNativeLibrary.INSTANCE.sayHello("ITeaConf");
  84. 121 JNA __declspec(dllexport) void __stdcall sayHello(const char* name) { printf("Hello

    %s from native!\n", name); } MyNativeLib.c public interface MyNativeLibrary extends Library { MyNativeLibrary INSTANCE = (MyNativeLibrary) Native.load("MyNativeLib", MyNativeLibrary.class); void sayHello(String name); } TestJNA.java MyNativeLibrary.INSTANCE.sayHello("ITeaConf");
  85. 122 JNA __declspec(dllexport) void __stdcall sayHello(const char* name) { printf("Hello

    %s from native!\n", name); } MyNativeLib.c public interface MyNativeLibrary extends Library { MyNativeLibrary INSTANCE = (MyNativeLibrary) Native.load("MyNativeLib", MyNativeLibrary.class); void sayHello(String name); } MyNativeLibrary.INSTANCE.sayHello("ITeaConf"); TestJNA.java $ java -Djava.library.path=./lib TestJNA Hello ITeaConf from native!
  86. 123 JNA Поддерживается: ✓ Передача, возврат по значению ✓ Указатели,

    C-like массивы, C-like строки ✓ Указатели на функции ✓ Struct & Union ✓ varargs
  87. 124 JNA Самое вкусное: ✓ Заготовлены Java описания для многих

    популярных C библиотек ◦ LibC, X11, udev, ... ◦ Kernel32, Pdh, Psapi, ... github.com/java-native-access/jna#jna-platform
  88. 126 JNA WinBase.SYSTEMTIME systemTime = new WinBase.SYSTEMTIME(); Kernel32.INSTANCE.GetSystemTime(systemTime); System.out.println("System time:

    " + systemTime); Psapi.PERFORMANCE_INFORMATION info = new Psapi.PERFORMANCE_INFORMATION(); Psapi.INSTANCE.GetPerformanceInfo(info, 104); System.out.println("commited mem = " + info.CommitTotal); System.out.println("physical mem = " + info.PhysicalTotal); commited mem = 3357974 physical mem = 2069169 System time: 31 октября 2021 г., 14:08:03
  89. 129 JNA - подводные камни Java ⇒ Java - прямой

    вызов Java метода без инлайна Java ⇒ Native - вызов нативного метода (без параметров) из Java Разница в 6 раз OpenJDK 17.0.1
  90. 130 JNA - подводные камни Java ⇒ Native (JNI) -

    вызов нативного метода (без параметров) из Java Java ⇒ Native (JNA) - вызов нативного метода чрез JNA OpenJDK 17.0.1
  91. 131 JNA - подводные камни Java ⇒ Native (JNI) -

    вызов нативного метода (без параметров) из Java Java ⇒ Native (JNA) - вызов нативного метода чрез JNA Разница в 7.5 раз (в 42 раза от вызова Java версии) OpenJDK 17.0.1
  92. Как позвать натив? 133 Как себя должен вести GC? Где

    взять нативы? Как работать с Java из натива?
  93. Как позвать натив? 134 Как себя должен вести GC? Где

    взять нативы? Как работать с Java из натива?
  94. Производительность JNA 135 ✓ Все базируется на JNI, поэтому быстрее

    быть точно не может ✓ Поиск реализаций: на стороне Java - Reflection, на стороне натива огромный dispatch MyNativeLibrary.INSTANCE.sayHello("ITeaConf"); com.sun.jna.Natives.invokeVoid(...) com.sun.jna.Function.invoke(...) void __stdcall sayHello(const char* name)
  95. Производительность JNA 136 ✓ Все базируется на JNI, поэтому быстрее

    быть точно не может ✓ Поиск реализаций: на стороне Java - Reflection, на стороне натива огромный dispatch ✓ Мусорит Java обертками вокруг нативых сущностей (Pointer, Memory, ByReference)
  96. А есть еще варианты? 139 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA
  97. А есть еще варианты? 143 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA (но не быстрее JNI)
  98. А есть еще варианты? 144 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA (но не быстрее JNI) ◦ но меньше оберток для библиотек и работает не на всех платформах
  99. А есть еще варианты? 145 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA (но не быстрее JNI) ◦ но меньше оберток для библиотек и работает не на всех платформах ✓ JavaCPP
  100. А есть еще варианты? 146 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA (но не быстрее JNI) ◦ но меньше оберток для библиотек и работает не на всех платформах ✓ JavaCPP ◦ работает намного* быстрее JNA (но не быстрее JNI)
  101. А есть еще варианты? 148 ✓ JNR - Java Native

    Runtime ◦ работает НАМНОГО быстрее JNA (но не быстрее JNI) ◦ но меньше оберток для библиотек и работает не на всех платформах ✓ JavaCPP ◦ работает намного* быстрее JNA (но не быстрее JNI) ◦ предназначен для связи с C++ кодом
  102. 1. JNA, JNR, JavaCPP - повышают удобство интеропа 2. С

    ними больше никакого кода на C/C++ 3. Платим производительностью и иногда корректностью 4. Детальный разбор здесь: https://www.youtube.com/watch?v=DVTeZdtuHS0 149 Takeaways про библиотеки
  103. 152 Project Panama ✓ Что: мега-проект в JDK (since 2014)

    ✓ Зачем: легкость использования С/С++ библиотек и нативного кода из Java ✓ Как: вместо написания кода на C/C++ расширить возможности Java
  104. 153 Project Panama ✓ Memory Access API (JEP 370, 383,

    393) ◦ Новый API для работы с нативной памятью ◦ Потенциальная замена ByteBuffer ◦ Incubator-модуль в Java 14, 15, 16, 17
  105. 154 Project Panama ✓ Memory Access API (JEP 370, 383,

    393) ✓ Новый Foreign Function Interface (JEP 191, 412, 419) ◦ Native Method Handles ◦ MemorySegment from Memory Access API ◦ jextract для генерации интерфейсов ◦ Incubator-модуль в Java 17
  106. 155 Project Panama ✓ Memory Access API (JEP 370, 383,

    393) ✓ Новый Foreign Function Interface (JEP 191, 412, 419) ◦ Native Method Handles ◦ MemorySegment from Memory Access API ◦ jextract для генерации интерфейсов ◦ Incubator-модуль в Java 17 ✓ Vector API (JEP 338, 414)
  107. Project Panama void test(void) { printf("Hello from Panama!\n"); } void

    testUpcall(void (*upcall)(void)) { upcall(); } panamatest.c void test(void); void testUpcall(void (*upcall)(void)); panamatest.h 156
  108. static { System.loadLibrary("panamatest"); } static CLinker LINKER = CLinker.getInstance(); static

    SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); static final MethodHandle downcallMH = LINKER.downcallHandle(loaderLookup.lookup("test").get(), MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); Project Panama 157 jdk-17.0.1
  109. static { System.loadLibrary("panamatest"); } static CLinker LINKER = CLinker.getInstance(); static

    SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); static final MethodHandle downcallMH = LINKER.downcallHandle(loaderLookup.lookup("test").get(), MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); Project Panama 158 jdk-17.0.1
  110. static { System.loadLibrary("panamatest"); } static CLinker LINKER = CLinker.getInstance(); static

    SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); static final MethodHandle downcallMH = LINKER.downcallHandle(loaderLookup.lookup("test").get(), MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); Project Panama 159 downcallMH.invokeExact(); jdk-17.0.1
  111. static { System.loadLibrary("panamatest"); } static CLinker LINKER = CLinker.getInstance(); static

    SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); static final MethodHandle downcallMH = LINKER.downcallHandle(loaderLookup.lookup("test").get(), MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); Project Panama 160 downcallMH.invokeExact(); $ java --add-modules=jdk.incubator.foreign \ --enable-native-access=ALL-UNNAMED \ -Djava.library.path=./lib TestPanama jdk-17.0.1
  112. static { System.loadLibrary("panamatest"); } static CLinker LINKER = CLinker.getInstance(); static

    SymbolLookup loaderLookup = SymbolLookup.loaderLookup(); static final MethodHandle downcallMH = LINKER.downcallHandle(loaderLookup.lookup("test").get(), MethodType.methodType(void.class), FunctionDescriptor.ofVoid()); Project Panama 161 downcallMH.invokeExact(); $ java --add-modules=jdk.incubator.foreign \ --enable-native-access=ALL-UNNAMED \ -Djava.library.path=./lib TestPanama WARNING: Using incubator modules: jdk.incubator.foreign Hello from Panama! jdk-17.0.1
  113. Project Panama 162 void test(void) { printf("Hello from Panama!\n"); }

    void testUpcall(void (*upcall)(void)) { upcall(); } panamatest.c void test(void); void testUpcall(void (*upcall)(void)); panamatest.h Возвращаемся в Java код через указатель на Java функцию;
  114. Project Panama 163 void test(void) { printf("Hello from Panama!\n"); }

    void testUpcall(void (*upcall)(void)) { upcall(); } panamatest.c void test(void); void testUpcall(void (*upcall)(void)); panamatest.h Возвращаемся в Java код через указатель на Java функцию; Java объекты в нативный код больше не попадают! (кроме ну очень специальных, типа MemoryAddress)
  115. Project Panama: jextract void test(void) { printf("Hello from Panama!\n"); }

    void testUpcall(void (*upcall)(void)) { upcall(); } panamatest.c void test(void); void testUpcall(void (*upcall)(void)); panamatest.h 164
  116. Project Panama: jextract void test(void) { printf("Hello from Panama!\n"); }

    void testUpcall(void (*upcall)(void)) { upcall(); } panamatest.c jextract void test(void); void testUpcall(void (*upcall)(void)); panamatest.h 165
  117. package org.sample; public final class panamatest_h { public static MethodHandle

    test$MH() { return panamatest_h$constants.test$MH(); } public static void test () { try { panamatest_h$constants.test$MH().invokeExact(); } catch (Throwable ex) { throw new AssertionError(ex); } } ... } panamatest_h.java Project Panama: jextract 166
  118. 168 Project Panama Java ⇒ Native (JNI) - вызов нативного

    метода (без параметров) из Java OpenJDK 16-internal+0-adhoc.user.panama-foreign-CallIntrinsicsFinal2
  119. 169 Project Panama Java ⇒ Native (JNI) - вызов нативного

    метода (без параметров) из Java Java ⇒ Native (Panama) - вызов нативного метода (без параметров) из Java через MethodHandle +4% OpenJDK 16-internal+0-adhoc.user.panama-foreign-CallIntrinsicsFinal2
  120. 170 Project Panama Java ⇒ Native (JNI) - вызов нативного

    метода (без параметров) из Java Java ⇒ Native (Panama) - вызов нативного метода (без параметров) из Java через MethodHandle +6.7% OpenJDK 17.0.1
  121. 171 Project Panama Java ⇒ Native ⇒ Java (JNI) -

    upcall в Java через вызов метода переданного объекта Java ⇒ Native ⇒ Java (Panama 17) - upcall через указатель на функцию и MethodHandle +8% OpenJDK 17.0.1
  122. 172 Project Panama: что читать ✓ Мейлинг лист проекта: mail.openjdk.java.net/mailman/listinfo/panama-dev

    ✓ Описание текущего состояния: github.com/openjdk/panama-foreign/...
  123. Заключение ✓ Старайтесь не писать нативный код (там горные тролли)

    ✓ Знайте свой путь в Мордор фреймворк для вызова нативного кода youtube.com/watch?v=DVTeZdtuHS0 175
  124. Заключение ✓ Старайтесь не писать нативный код (там горные тролли)

    ✓ Знайте свой путь в Мордор фреймворк для вызова нативного кода youtube.com/watch?v=DVTeZdtuHS0 ✓ Native ⇔ managed переходы в Java - все еще открытый вопрос! 176