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

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

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

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

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

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

7ceba6b127183d8699a23b597cfff18e?s=128

Ivan Ugliansky

June 30, 2020
Tweet

Transcript

  1. В нативный код из уютного мира Java Путешествие Туда и

    Обратно Иван Углянский Excelsior@Huawei
  2. Иван Углянский 2 JVM engineer at Excelsior@Huawei ivan.ugliansky@gmail.com @dbg_nsk JUGNsk

    co-lead
  3. Какой еще нативный код? 3

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

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

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

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

    native void goNative(); static native void goThere(Callback andBackAgain); } Java goThere andBackAgain C/C++/...
  8. Но зачем нам нативы? 8

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

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

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

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

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

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

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

  16. Но знаете, как это бывает... 16

  17. Причины звать нативный код из Java 17 1. Есть отличная

    библиотека! (Но она на С/C++)
  18. Причины звать нативный код из Java 18 1. Есть отличная

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

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

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

    этого не нужно, но JDK тоже зовет нативный код!
  22. 22

  23. 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
  24. 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]
  25. 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
  26. 26 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
  27. Будем разбираться 27

  28. Будем разбираться 28 1. Почему так много проблем с нативами?

  29. Будем разбираться 29 1. Почему так много проблем с нативами?

    2. Как пройти в Мордор и не получить SIGSEGV?
  30. Как позвать натив? 30

  31. Как позвать натив? 31 Где взять нативы?

  32. Как позвать натив? 32 Где взять нативы? Как работать с

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

    взять нативы? Как работать с Java из натива?
  34. Немного истории 34

  35. 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. ... История до нашей эры 35
  36. Наша эра 36 JNI - Java Native Interface

  37. Наша эра 37 JNI - Java Native Interface ✓ Единый

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

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

  40. Как это выглядит со стороны 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")); } }
  41. Как это выглядит со стороны 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")); } }
  42. Как это выглядит со стороны Java? 42 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")); } }
  43. Как это выглядит со стороны Java? 43 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); } }
  44. Как это выглядит со стороны С? 44

  45. Как это выглядит со стороны С? 45 public class JavaToNative

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

    { static native void goThere(Callback andBackAgain); } javac JavaToNative.java -h .
  47. Как это выглядит со стороны С? 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
  48. Как это выглядит со стороны С? 48 public class JavaToNative

    { static native void goThere(Callback andBackAgain); } JNIEXPORT void JNICALL Java_JavaToNative_goThere (JNIEnv *, jclass, jobject); javac JavaToNative.java -h . JavaToNative.h
  49. Как это выглядит со стороны С? 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 Примитивные
  50. Как это выглядит со стороны С? 50 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 ...
  51. Как это выглядит со стороны С? 51 public class JavaToNative

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

  53. Как это выглядит со стороны С? 53 JNIEnv ✓ Указатель

    на JNINativeInterface (214 функций)
  54. Как это выглядит со стороны С? 54 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, ...
  55. Как это выглядит со стороны С? 55 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
  56. Как это выглядит со стороны С? 56 JNIEnv ✓ Указатель

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

    JavaToNative * Method: goThere * Signature: (LCallback;)V */ JNIEXPORT void JNICALL Java_JavaToNative_goThere(JNIEnv * env, jclass klass, jobject andBackAgain) { } JavaToNative.c
  58. Как это выглядит со стороны С? 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"); } JavaToNative.c
  59. Как это выглядит со стороны С? 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"); } JavaToNative.c
  60. Как это выглядит со стороны С? 60 /* * 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
  61. Собираем! 61

  62. Собираем! 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
  63. Собираем! 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 Nokee plugins ✓ Кроссплатформенное решение ✓ Удобное использование через Gradle ✓ nokee.dev
  64. Собираем и запускаем! 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")); } }
  65. Собираем и запускаем! 65 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
  66. Что может пойти не так? 66

  67. Что может пойти не так? 67 ✓ Статическая типовая информация

    утеряна. Добро пожаловать в JavaScript!
  68. 68 /* * 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); }
  69. 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
  70. 70 /* * 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]
  71. Что может пойти не так? 71 ✓ Статическая типовая информация

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

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

  74. 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 -Djava.library.path=./lib JavaToNative
  75. 75 /* * 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
  76. 76 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)
  77. Что может пойти не так? 77 ✓ Статическая типовая информация

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

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

    потоки
  80. 80 ◦ safe-points, в которых "припарковываются" потоки ◦ GC ждет,

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

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

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

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

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

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

    на входе и выходе из натива синхронизация с GC Threads in Java code GC Threads GC request Threads in native GC в нативном коде
  87. 87 Нельзя трогать Java объекты в нативе во время GC!

  88. 88 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 в нативном коде
  89. 89 jobject и компания - специальные хендлы, для которых: 1.

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

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

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

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

  94. Подводные камни (GC + Natives) 94 ✓ Для хендлов реализована

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

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

    альтернативная система управления памятью Типы хендлов (в коде все выглядит, как jobject & Co): 1. Local Reference
  97. Подводные камни (GC + Natives) 97 Local Reference: 1. Существует

    не дольше, чем исполняется натив 2. Аргументы нативов и возвращаемое значение многих JNI функций
  98. Local Reference: 1. Существует не дольше, чем исполняется натив 2.

    Аргументы нативов и возвращаемое значение многих JNI функций 3. Отличный источник утечек памяти! Подводные камни (GC + Natives) 98
  99. Подводные камни (GC + Natives) 99 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); }
  100. Подводные камни (GC + Natives) JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest (JNIEnv

    *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); } 100
  101. Подводные камни (GC + Natives) 101 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); }
  102. Подводные камни (GC + Natives) 102 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); }
  103. Подводные камни (GC + Natives) 103 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); if (!obj) { printf("allocation attempt %d failed\n", id); } ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); }
  104. Подводные камни (GC + Natives) 104 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); if (!obj) { printf("allocation attempt %d failed\n", id); } ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); } public static void main(String[] args) { System.loadLibrary("NativeLib"); objectsAllocationTest(); } java -Xmx1G -Djava.library.path=./lib JavaToNative
  105. Подводные камни (GC + Natives) 105 JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest

    (JNIEnv *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); if (!obj) { printf("allocation attempt %d failed\n", id); } ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; } printf("finally ready after %d objects created!\n", id); } public static void main(String[] args) { System.loadLibrary("NativeLib"); objectsAllocationTest(); } java -Xmx1G -Djava.library.path=./lib JavaToNative
  106. Подводные камни (GC + Natives) JNIEXPORT void JNICALL Java_JavaToNative_objectsAllocationTest (JNIEnv

    *env, jclass klass) { jclass cls = (*env)->FindClass(env, "BornInNative"); jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); jmethodID check = (*env)->GetMethodID(env, cls, "areYouReady", "()Z"); int ready = 0, id = 0; while (!ready) { jobject obj = (*env)->NewObject(env, cls, init, id++); if (!obj) { printf("allocation attempt %d failed\n", id); } ready = (*env)->CallBooleanMethod(env, obj, check) == JNI_TRUE; (*env)->DeleteLocalRef(env, obj); } printf("finally ready after %d objects created!\n", id); } 106
  107. Подводные камни (GC + Natives) 107 ✓ Для хендлов реализована

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

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

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

    памятью (в которой легко получить memory leak/dangling pointer) ✓ Особая обработка массивов и строк: pinning vs copying jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* isCopy); void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elems, jint mode);
  111. Подводные камни (GC + Natives) 111 ✓ Особая система управления

    памятью (в которой легко получить memory leak/dangling pointer) ✓ Особая обработка массивов и строк: pinning vs copying jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* isCopy); void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elems, jint mode); на самом деле копирование происходит всегда (для большинства JVM/GC)
  112. Подводные камни (GC + Natives) 112 ✓ Особая система управления

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

  114. Производительность нативных вызовов 114 ВСЕ ОЧЕНЬ МЕДЛЕННО

  115. Производительность нативных вызовов 115 ВСЕ ОЧЕНЬ МЕДЛЕННО Измерять будем на:

    Intel Core i7-7700 @ 3.60 GHz; 16GB RAM; Linux Ubuntu 18.04
  116. Производительность нативных вызовов 116 прямой вызов Java метода без инлайна

    Java Java
  117. Производительность нативных вызовов 117 прямой вызов Java метода без инлайна

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

    вызов нативного метода (без параметров) из Java Разница в 3.3 раза На jdk8u252 Java Java Java Native OpenJDK 1.8.0_252
  119. Производительность нативных вызовов 119 прямой вызов Java метода без инлайна

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

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

    (без параметров) из Java, а из него вызов Java метода (без параметров) Разница в 10 раз Производительность нативных вызовов 121 Java Native Java Java Native OpenJDK 11.0.7
  122. Производительность нативных вызовов ...почему? ВСЕ ОЧЕНЬ МЕДЛЕННО

  123. 123 static native void goNative(); call 0x00007f4882317c70

  124. 124 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
  125. Производительность нативных вызовов 125 ✓ State transition (Java -> Native

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

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

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

  129. 129 А МОЖЕТ НЕ НУЖНО ПИСАТЬ КОД НА C/C++?

  130. Идея: ✓ Весь код писать на Java, а связь с

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

    нативом генерировать автоматически Реализация: ✓ Библиотеки: JNA, JNR, JavaCPP, ... 131 Новейшее время
  132. (Java Native Access) JNA <dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.5.0</version> </dependency> 132

  133. 133 JNA __declspec(dllexport) void __stdcall sayHello(const char* name) { printf("Hello

    %s from native!\n", name); } MyNativeLib.c
  134. 134 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("JPoint"); TestJNA.java
  135. 135 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("JPoint"); TestJNA.java
  136. 136 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("JPoint"); TestJNA.java $ java -Djava.library.path=./lib TestJNA Hello JPoint from native!
  137. 137 JNA Поддерживается: ✓ Передача, возврат по значению ✓ Указатели,

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

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

    " + systemTime); System time: 24 мая 2020 г., 14:08:03
  140. 140 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: 24 мая 2020 г., 14:08:03
  141. 141 JNA - подводные камни

  142. 142 JNA - подводные камни ВСЁ ЕЩЁ МЕДЛЕННЕЕ

  143. 143 JNA - подводные камни Java ⇒ Java - прямой

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

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

    вызов нативного метода (без параметров) из Java Java ⇒ Native (JNA) - вызов нативного метода чрез JNA Разница в 8.5 раз (в 50 раз от вызова Java версии) OpenJDK 11.0.7
  146. Производительность JNA 146 ✓ Все базируется на JNI, поэтому быстрее

    быть точно не может
  147. Как позвать натив? 147 Как себя должен вести GC? Где

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

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

    быть точно не может ✓ На стороне Java - Reflection, на стороне натива огромный dispatch MyNativeLibrary.INSTANCE.sayHello("JPoint"); com.sun.jna.Natives.invokeVoid(...) com.sun.jna.Function.invoke(...) void __stdcall sayHello(const char* name)
  150. 150 JNA - подводные камни ✓ JNA мусорит Java обертками

    вокруг нативых сущностей (Pointer, Memory, ByReference) ◦ Нагрузка на GC, ◦ Ухудшение производительности,
  151. 151 JNA - подводные камни ✓ JNA мусорит Java обертками

    вокруг нативых сущностей (Pointer, Memory, ByReference) ◦ Нагрузка на GC, ◦ Ухудшение производительности, ◦ И даже некорректное поведение!
  152. 152

  153. 153 Memory finalize()

  154. 154 StringByReference Memory finalize() finalize() пользовательский код

  155. 155 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; пользовательский код
  156. 156 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; Чей финализатор вызовется первым? пользовательский код
  157. 157 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; пользовательский код случился первым
  158. 158 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; пользовательский код случился первым утечка памяти!
  159. 159 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; пользовательский код случился первым И в addr лежит мусор...
  160. 160 StringByReference Memory finalize() finalize() if (addr != null) {

    GlobalFree(addr.getPointer(0)); } free(addr); addr = null; пользовательский код случился первым И в addr лежит мусор… получаем спорадичный развал из-за вызова GlobalFree от битой памяти!
  161. ◦ Нагрузка на GC, ◦ Ухудшение производительности, ◦ И даже

    некорректное поведение! 161 JNA - подводные камни ✓ JNA мусорит Java обертками вокруг нативых сущностей (Pointer, Memory, ByReference) ✓ Недетерминированное поведение и спорадичные развалы JVM
  162. Что же выбрать? 162

  163. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 163
  164. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 164
  165. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 165
  166. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 166
  167. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 167
  168. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 168
  169. (Java Native Runtime) JNR <dependency> <groupId>com.github.jnr</groupId> <artifactId>jnr-ffi</artifactId> <version>2.1.14</version> </dependency> 169

  170. 170 JNR __attribute((visibility("default"))) void sayHello(const char* name) { printf("Hello %s

    from native!\n", name); } MyNativeLib.c
  171. 171 JNR __attribute((visibility("default"))) void sayHello(const char* name) { printf("Hello %s

    from native!\n", name); } MyNativeLib.c public static interface MyNativeLib { void sayHello(String s); } MyNativeLib myLib = LibraryLoader.create(MyNativeLib.class).load("MyNativeLib"); myLib.sayHello("JPoint"); TestJNR.java
  172. 172 JNR __attribute((visibility("default"))) void sayHello(const char* name) { printf("Hello %s

    from native!\n", name); } MyNativeLib.c public static interface MyNativeLib { void sayHello(String s); } MyNativeLib myLib = LibraryLoader.create(MyNativeLib.class).load("MyNativeLib"); myLib.sayHello("JPoint"); TestJNR.java $ java -Djava.library.path=./lib TestJNR Hello JPoint from native!
  173. 173 JNR __attribute((visibility("default"))) void sayHello(const char* name) { printf("Hello %s

    from native!\n", name); } MyNativeLib.c public static interface MyNativeLib { void sayHello(String s); } MyNativeLib myLib = LibraryLoader.create(MyNativeLib.class).load("MyNativeLib"); myLib.sayHello("JPoint"); TestJNR.java yet another JNA?
  174. JNR - производительность 174 OpenJDK 11.0.7

  175. JNR - производительность 175 OpenJDK 11.0.7

  176. JNR - производительность 176 public static interface MyNativeLib { @IgnoreError

    void sayHello(String s); } TestJNR.java
  177. JNR - производительность 177 OpenJDK 11.0.7

  178. Как позвать натив? 178 Как себя должен вести GC? Где

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

    быть точно не может
  180. Производительность JNR 180 ✓ Все базируется на JNI, поэтому быстрее

    быть точно не может ✓ На стороне Java - сгенерированный байткод, на стороне натива сгенерированный асм! myLib.sayHello("JPoint"); asm wrapper (super lightweight) bytecode wrapper (inlined) void sayHello(const char* name)
  181. 181 JNR: ✓ используется в JRuby и активно развивается ✓

    есть готовая обвязка для posix, unisocket, signal...
  182. ✓ используется в JRuby и активно развивается ✓ есть готовая

    обвязка для posix, unisocket, signal... Подводные камни: 182 ✓ асм завертки генерируются не для всех платформ. На Windows их нет вообще ✓ документации почти нет JNR:
  183. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 183
  184. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 184
  185. JavaCPP <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>1.5.3</version> </dependency> 185

  186. 186 JavaCPP #include <iostream> inline void sayHelloFromCPP(std::string name) { std::cout

    << "Hello " << name << " from cpp code!" << std::endl; } MyCPPLib.cpp
  187. 187 JavaCPP #include <iostream> inline void sayHelloFromCPP(std::string name) { std::cout

    << "Hello " << name << " from cpp code!" << std::endl; } MyCPPLib.cpp @Platform(include = "MyCPPLib.cpp") public static class MyCPPLib { static { Loader.load(); } static native void sayHelloFromCPP(@StdString String name); } MyCPPLib.sayHelloFromCPP("JPoint"); JavaCppTest.java
  188. 188 JavaCPP #include <iostream> inline void sayHelloFromCPP(std::string name) { std::cout

    << "Hello " << name << " from cpp code!" << std::endl; } MyCPPLib.cpp @Platform(include = "MyCPPLib.cpp") public static class MyCPPLib { static { Loader.load(); } static native void sayHelloFromCPP(@StdString String name); } MyCPPLib.sayHelloFromCPP("JPoint"); JavaCppTest.java $ java -Djava.library.path=./lib JavaCppTest Hello JPoint from cpp code!
  189. 189 JavaCPP ✓ Позволяет из Java работать с C++ сущностями:

    ◦ виртуальные функции и перегруженные операторы ◦ string, vector, map ◦ указатели, в т.ч. на функции ◦ деструкторы и конструкторы ◦ smart-pointers ◦ шаблоны!
  190. 190 JavaCPP ✓ Позволяет из Java работать с C++ сущностями

    ✓ Гибкая генерация Java оберток по C++ библиотеке ✓ Огромное количество заготовленных оберток для популярных библиотек github.com/bytedeco/javacpp-presets
  191. 191 JavaCPP ✓ Позволяет из Java работать с C++ сущностями

    ✓ Гибкая генерация Java оберток по C++ библиотеке ✓ Огромное количество заготовленных оберток для популярных библиотек ✓ Сборка через Maven или Gradle, включая C/C++ часть! github.com/bytedeco/javacpp-presets
  192. 192 JavaCPP - подводные камни А что там с производительностью?

  193. 193 JavaCPP - подводные камни ✓ Все базируется на JNI,

    поэтому быстрее быть точно не может
  194. 194 JavaCPP #include <iostream> inline void sayHelloFromCPP(std::string name) { std::cout

    << "Hello " << name << " from cpp code!" << std::endl; } @Platform(include = "MyCPPLib.cpp") public static class MyCPPLib { static { Loader.load(); } static native void sayHelloFromCPP (@StdString String name); } MyCPPLib.sayHelloFromCPP("JPoint"); JNIEXPORT void JNICALL Java_MyCPPLib_sayHelloFromCPP (JNIEnv *, jclass, jstring); сгенерированные JNI адаптеры компилируются в отдельную нативную библиотеку при сборке
  195. 195 JavaCPP - подводные камни OpenJDK 11.0.7

  196. 196 JavaCPP - подводные камни но стоит заиспользовать C++ объекты

    в Java коде.. OpenJDK 11.0.7
  197. JavaCPP - подводные камни ✓ Использование Java версий C++ объектов

    просаживает производительность 197
  198. JavaCPP - подводные камни ✓ Использование Java версий C++ объектов

    просаживает производительность ✓ Вместо деструкторов и smart pointers - PhantomReference + ReferenceQueue недетерменированность исполнения и веселые баги! (привет, JNA) 198
  199. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 199
  200. Takeaways про библиотеки Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 200
  201. 201 Что день готовит нам грядущий?

  202. 202 Project Panama

  203. 203 Project Panama ✓ Что: мега-проект в JDK (since 2014)

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

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

    383) ✓ Новый Foreign Function Interface (JEP 191) ◦ Native Method Handles ◦ MemorySegment from Memory Access API ◦ jextract для генерации интерфейсов
  206. 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 206
  207. Project Panama 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 207
  208. 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 208
  209. 209 А что там с производительностью? Project Panama

  210. 210 Project Panama Java ⇒ Native (JNI) - вызов нативного

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

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

    метода (без параметров) из Java Java ⇒ Native (Panama) - вызов нативного метода (без параметров) из Java через MethodHandle +4% OpenJDK 16-internal+0-adhoc.user.panama-foreign-CallIntrinsicsFinal2
  213. 213 Project Panama ✓ Улучшения производительности Java ⇒ Native и

    Native ⇒ Java вызовов через MethodHandles WORK IN PROGRESS!
  214. 214 Project Panama ✓ Улучшения производительности Java ⇒ Native и

    Native ⇒ Java вызовов через MethodHandles WORK IN PROGRESS! ✓ Unsafe нативные методы, которые блокируют GC WORK IN PROGRESS!
  215. 215 Project Panama (подводные камни) Threads in Java code GC

    Threads GC request Threads in native
  216. 216 Project Panama (подводные камни) Threads in Java code GC

    Threads GC request Threads in native Threads in unsafe native
  217. 217 Project Panama (подводные камни) Threads in Java code GC

    Threads GC request Threads in native Threads in unsafe native Здесь не может произойти STW!
  218. 218 Project Panama Java ⇒ Native (JNI) - вызов нативного

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

    метода (без параметров) из Java через MethodHandle без синхронизации с GC OpenJDK 16-internal+0-adhoc.user.panama-foreign-CallIntrinsicsFinal2
  220. 220 Project Panama ✓ Unsafe natives - опасный механизм, но

    дает уникальную производительность ✓ По умолчанию вызовы безопасные ✓ Очень экспериментальная фича! (замеры на версии от 25.06.20)
  221. 221 Project Panama youtu.be/4vHMmLqF09Y youtu.be/0y6_RDga-fk

  222. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP 222
  223. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP Panama 223
  224. А может полетим в Мордор на орлах? 224

  225. 225

  226. Truffle Framework 226

  227. Truffle Framework GraalVM LLVM Runtime 227

  228. GraalVM LLVM Runtime ✓ C/C++ код компилируется в LLVM bitcode

    228
  229. GraalVM LLVM Runtime ✓ C/C++ код компилируется в LLVM bitcode

    void goNative() { printf("Hello from LLVM!\n"); } native.c $LLVM_TOOLCHAIN/clang -c native.c -o native.bc native.bc 229
  230. GraalVM LLVM Runtime ✓ C/C++ код компилируется в LLVM bitcode

    ✓ LLVM bitcode интерпретируется с агрессивной специализацией 230
  231. import org.graalvm.polyglot.*; ... Context polyglot = Context.newBuilder().allowAllAccess(true).build(); File file =

    new File("native.bc"); Source source = Source.newBuilder("llvm", file).build(); Value cpart = polyglot.eval(source); cpart.getMember("goNative").executeVoid(); TestPolyglot.java 231
  232. import org.graalvm.polyglot.*; ... Context polyglot = Context.newBuilder().allowAllAccess(true).build(); File file =

    new File("native.bc"); Source source = Source.newBuilder("llvm", file).build(); Value cpart = polyglot.eval(source); cpart.getMember("goNative").executeVoid(); TestPolyglot.java $ $GRAAL_VM_HOME/javac TestPolyglot.java $ $GRAAL_VM_HOME/java TestPolyglot Hello from LLVM! 232
  233. GraalVM LLVM Runtime ✓ C/C++ код компилируется в LLVM bitcode

    ✓ LLVM bitcode интерпретируется с агрессивной специализацией ✓ Единое внутреннее представление для native и Java кода ✓ Native код под нашим контролем! 233
  234. 234 А что там с производительностью? GraalVM LLVM Runtime

  235. 235 GraalVM LLVM Runtime Java ⇒ Native - вызов пустого

    нативного метода (без параметров) из Java OpenJDK GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
  236. 236 GraalVM LLVM Runtime Java ⇒ Native - вызов пустого

    нативного метода (без параметров) из Java Sulong - вызов пустого метода на C (без параметров) через Sulong OpenJDK GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
  237. GraalVM LLVM Runtime int nativeWithSomeWork(int k) { int prev =

    0; int curr = 1; for (int i = 0; i < k; i++) { int summ = prev + curr; prev = curr; curr = summ; } return curr; } native.c $LLVM_TOOLCHAIN/clang -O2 -c native.c -o native.bc native.bc 237
  238. 238 GraalVM LLVM Runtime Java ⇒ Native - вызов пустого

    нативного метода (без параметров) из Java Sulong - вызов пустого метода на C (без параметров) через Sulong OpenJDK GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
  239. 239 GraalVM LLVM Runtime Java ⇒ Native - вызов пустого

    нативного метода (без параметров) из Java Sulong - вызов пустого метода на C (без параметров) через Sulong (fib100) - вычисление 100- ого числа Фибоначчи в нативе ↓x2.5 OpenJDK GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
  240. 240 GraalVM LLVM Runtime Java ⇒ Native - вызов пустого

    нативного метода (без параметров) из Java Sulong - вызов пустого метода на C (без параметров) через Sulong (fib100) - вычисление 100- ого числа Фибоначчи в нативе ↓x2.5 vs ↓x5 OpenJDK GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
  241. 241 GraalVM LLVM Runtime ✓ Чем больше нативного кода, тем

    хуже производительность ✓ Sulong - work in progress ✓ Sulong нужно очень долго “прогревать”
  242. Что же выбрать? Удобство использования Производительность Надежность Документированность Работает с

    C++ Заготовки для библиотек JNI JNA JNR JavaCPP Panama 242
  243. Удобство использования Производительность Надежность Документированность Работает с C++ Заготовки для

    библиотек JNI JNA JNR JavaCPP Panama Sulong Финальное сравнение 243
  244. Заключение ✓ Старайтесь не писать нативный код (там горные тролли)

    ✓ Знайте свой путь в Мордор фреймворк для вызова нативного кода ✓ Native ⇔ managed переходы в Java - все еще открытый вопрос! 244
  245. 245 Почему jdk8 такая быстрая: bugs.openjdk.java.net/browse/JDK-8187809 jdk11 jdk16 GraalVM jdk11

    jdk8
  246. Q & A @jugnsk ivan.ugliansky@gmail.com @dbg_nsk бенчмарки здесь: github.com/ugliansky/jpoint2020