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

JUGNsk Meetup #2. Григорий Кошелев: "Интеграция виртуальных машин .NET и Java"

JUGNsk Meetup #2. Григорий Кошелев: "Интеграция виртуальных машин .NET и Java"

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

И продолжим экспериментировать с интеграцией виртуальных машин. Зачем и как мы это делаем, с какими сложностями сталкиваемся и как их решаем — всё это в первом докладе встречи.

jugnsk

June 21, 2018
Tweet

More Decks by jugnsk

Other Decks in Programming

Transcript

  1. ИНТЕГРАЦИЯ ВИРТУАЛЬНЫХ МАШИН .NET И JAVA JUG Nsk, Meetup #2

    Григорий Кошелев Новосибирск, 21 июня 2018
  2. • Микросервисы • .NET и Java • С++, нативный код

    и прочие «кишки» О чѐм поговорим 2
  3. • Задачи • Радикальные способы решения • Интеграция: Акт I

    (микросервисы) • Интеграция: Акт II (Java Inside) План (крупно) 3
  4. • Задачи • Радикальные способы решения • Интеграция: Акт I

    (микросервисы) • Интеграция: Акт II (Java Inside) • Интеграция: Акт III (Deep Integration) План (крупно) 3
  5. • Задачи • Радикальные способы решения • Интеграция: Акт I

    (микросервисы) • Интеграция: Акт II (Java Inside) • Интеграция: Акт III (Deep Integration) • Планы на будущее План (крупно) 3
  6. • СКБ Контур: 85% стека – .NET • Докладчик: 85%

    стека – Java Дисклеймер 4 ПОЕХАЛИ?
  7. Очень много XML И всѐ это надо конвертировать в PDF

    Решение: Apache FOP (XSL-FO + XML -> PDF) Задача №1 6
  8. • Начало разработки – неизвестно • Передана в ASF в

    1999 году • Apache FOP 1.0 – 01.07.2011 • Apache FOP 1.1 – 16.10.2012 • Apache FOP 2.0 – 02.06.2015 • Apache FOP 2.1 – 15.01.2016 • Apache FOP 2.2 – 10.04.2017 Apache FOP 7
  9. Bouncy Castle 9 Версия Первый релиз Актуальный Java 1.59 xx.05.2000

    18.08.2017 09.01.2018 C# 1.8.1 13.10.2003 22.11.2015 28.12.2015
  10. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# Радикальные способы 13
  11. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# Радикальные способы 14
  12. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# • Сконвертировать код какой-нибудь тулзой, а потом яростно допиливать Радикальные способы 14
  13. • Where can I find a Java to C# converter?

    (StackOverflow, 2009) Конвертация кода Java -> C# 15
  14. • Where can I find a Java to C# converter?

    (StackOverflow, 2009) Конвертация кода Java -> C# 15
  15. • Convert Java to C# with a tool, or manually?

    (StackOverflow, 2011) Конвертация кода Java -> C# 16
  16. • Convert Java to C# with a tool, or manually?

    (StackOverflow, 2011) Конвертация кода Java -> C# 16
  17. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# • Сконвертировать код какой-нибудь тулзой, а потом яростно допиливать Радикальные способы 17
  18. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# • Сконвертировать код какой-нибудь тулзой, а потом яростно допиливать • Использовать кросс-компиляцию байткода Радикальные способы 17
  19. • IKVM.NET (https://www.ikvm.net/) • Реализация JVM под .NET • Реализация

    библиотеки классов Java в .NET • Транслятор байткода (jar -> dll) Кросс-компиляция байткода 18
  20. • IKVM.NET (https://www.ikvm.net/) • Реализация JVM под .NET • Реализация

    библиотеки классов Java в .NET • Транслятор байткода (jar -> dll) • IKVM 8.1 – 26.08.2015 • https://www.nuget.org/packages/IKVM/ Кросс-компиляция байткода 18
  21. • IKVM.NET (https://www.ikvm.net/) • Реализация JVM под .NET • Реализация

    библиотеки классов Java в .NET • Транслятор байткода (jar -> dll) • IKVM 8.1 – 26.08.2015 • https://www.nuget.org/packages/IKVM/ • The End of IKVM.NET (21.04.2017) Кросс-компиляция байткода 18
  22. • Удачный опыт: конвертация Java-клиента для ZooKeeper и обѐртки Curator;

    используется в продакшене Кросс-компиляция байткода 19
  23. • Удачный опыт: конвертация Java-клиента для ZooKeeper и обѐртки Curator;

    используется в продакшене • Неудачный опыт: конвертация Apache FOP; стало работать в 2 раза медленнее, выкинули Кросс-компиляция байткода 19
  24. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# • Сконвертировать код какой-нибудь тулзой, а потом яростно допиливать • Использовать кросс-компиляцию байткода Радикальные способы 20
  25. • Выучить Java и писать всѐ на ней • Переписать

    всѐ на C# • Сконвертировать код какой-нибудь тулзой, а потом яростно допиливать • Использовать кросс-компиляцию байткода Это всѐ разовые операции! Радикальные способы 20
  26. Модельный пример • Получение строки с хэшем по заданному алгоритму

    для заданного массива байтов • Пример: SHA-512, (byte)'a' 24
  27. Модельный пример • Получение строки с хэшем по заданному алгоритму

    для заданного массива байтов • Пример: SHA-512, (byte)'a' "1F40FC92DA241694750979EE6CF582F2D5D7D28E18 335DE05ABC54D0560E0F5302860C652BF08D560252A A5E74210546F369FBBBCE8C12CFC7957B2652FE9A75 " 24
  28. Тестирование • Intel Core i7-4710MQ, 2.5 ГГц • 12 ГБ

    ОЗУ • Windows 7 Professional x64 • Java 8 (1.8.0_161) • .NET 4.6.1 25
  29. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    28 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс
  30. Java HTTP REST JSON сервис C# КЛИЕНТ JVM HTTP REST

    JSON сервис 29 HttpClient DataContractJsonSerializer
  31. Java HTTP REST JSON сервис C# КЛИЕНТ JVM HTTP REST

    JSON сервис 29 Spring Boot 2.0.0.RELEASE
  32. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    30 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс JSON ? ? ?
  33. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    31 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс JSON 1,380 ± 0,040 мс 10,177 ± 0,117 мс 98,441 ± 1,820 мс
  34. • Библиотека для удалѐнного вызова процедур • Поддерживаются: C/C++, Node.js,

    Python, Ruby, Objective-C, PHP, C# и Java • Первый публичный релиз – 26.02.2015 • gRPC 1.0.0 – 19.08.2016 • gRPC 1.10.0 – 01.03.2018 • gRPC 1.12.0 – 09.05.2018 gRPC 32
  35. • Protobuf 3 – описание типов данных и сериализация •

    HTTP/2 в качестве транспорта gRPC изнутри 33
  36. • Protobuf 3 – описание типов данных и сериализация •

    HTTP/2 в качестве транспорта • Кодогенерация плагином для Protobuf gRPC изнутри 33
  37. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    35 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс gRPC ? ? ? JSON 1,380 ± 0,040 мс 10,177 ± 0,117 мс 98,441 ± 1,820 мс
  38. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    36 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс gRPC 155,9 ± 2,463 мкс 495,4 ± 7,676 мкс 5 417,8 ± 526,193 мкс JSON 1,380 ± 0,040 мс 10,177 ± 0,117 мс 98,441 ± 1,820 мс
  39. Лог бенчмарка 37 № итерации мс / оп 1 4,1672

    2 4,1738 … … 10 4,2284 11 8,8731 12 4,2938 13 6,5047 14 6,5204 15 4,5225
  40. • Появился в 1.1 • Позволяет вызывать нативный код из

    Java • Но это не всѐ! Есть Invocation API Java Native Interface 41
  41. • Появился в 1.1 • Позволяет вызывать нативный код из

    Java • Но это не всѐ! Есть Invocation API • Позволяет управлять JVM из нативного кода Java Native Interface 41
  42. Java Native Interface JavaVM *jvm; JNIEnv *env; JavaVMInitArgs args; JavaVMOption*

    options = new JavaVMOption[1]; options[0].optionString = params; args.nOptions = 1; args.options = options; args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = 0; int result = JNI_CreateJavaVM(&jvm, (void**)&env, &args); 42
  43. Java Native Interface JavaVM *jvm; JNIEnv *env; JavaVMInitArgs args; JavaVMOption*

    options = new JavaVMOption[1]; options[0].optionString = params; args.nOptions = 1; args.options = options; args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = 0; int result = JNI_CreateJavaVM(&jvm, (void**)&env, &args); 42 Осторожно! C++
  44. Java Native Interface JavaVM *jvm; JNIEnv *env; JavaVMInitArgs args; JavaVMOption*

    options = new JavaVMOption[1]; options[0].optionString = params; args.nOptions = 1; args.options = options; args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = 0; int result = JNI_CreateJavaVM(&jvm, (void**)&env, &args); 42 Осторожно! C++
  45. Java Native Interface JavaVM *jvm; JNIEnv *env; JavaVMInitArgs args; JavaVMOption*

    options = new JavaVMOption[1]; options[0].optionString = params; args.nOptions = 1; args.options = options; args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = 0; int result = JNI_CreateJavaVM(&jvm, (void**)&env, &args); 42 Осторожно! C++
  46. Java Native Interface JavaVM *jvm; JNIEnv *env; JavaVMInitArgs args; JavaVMOption*

    options = new JavaVMOption[1]; options[0].optionString = params; args.nOptions = 1; args.options = options; args.version = JNI_VERSION_1_6; args.ignoreUnrecognized = 0; int result = JNI_CreateJavaVM(&jvm, (void**)&env, &args); 42 Осторожно! C++
  47. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43
  48. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  49. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  50. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  51. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  52. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  53. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  54. package ru.kontur; public class Program { public static int doSmth(int

    x, String str) { return 0; } } Java Native Interface jclass programClass = env->FindClass("ru/kontur/Program"); jmethodID doSmthMethod = env->GetStaticMethodID(programClass, "doSmth", "(ILjava/lang/String;)I"); jint intParam = …; jstring stringParam = …; jint result = env->CallStaticIntMethod(programClass, doSmthMethod, intParam, stringParam); 43 Осторожно! C++
  55. gRPC снаружи vs gRPC внутри Среднее, мс Стд. откл, мс

    Относительное время Снаружи 73 065 2 367 100% Внутри 72 002 1 266 99% 46
  56. • Импорт Java-бинаря в .NET-процесс • Создаѐм JVM внутри .NET-процесса

    • Поднимаем gRPC-сервис План по реализации 48
  57. • Импорт Java-бинаря в .NET-процесс • Создаѐм JVM внутри .NET-процесса

    • Поднимаем gRPC-сервис План по реализации 49
  58. • Импорт Java-бинаря в .NET-процесс • Создаѐм JVM внутри .NET-процесса

    • Поднимаем gRPC-сервис План по реализации 51
  59. public unsafe void CreateJavaVm(IntPtr* vm, IntPtr* env, IntPtr args) {

    /* ... */ } Процедура создания JVM 52
  60. public unsafe void CreateJavaVm(IntPtr* vm, IntPtr* env, IntPtr args) {

    /* ... */ } Процедура создания JVM 52
  61. public unsafe void CreateJavaVm(IntPtr* vm, IntPtr* env, IntPtr args) {

    /* ... */ } Процедура создания JVM 52
  62. • Импорт Java-бинаря в .NET-процесс • Создаѐм JVM внутри .NET-процесса

    • Поднимаем gRPC-сервис План по реализации 53
  63. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  64. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  65. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  66. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  67. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  68. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  69. JavaVmWrapper vm; JniEnvWrapper env; unsafe { IntPtr envP; IntPtr vmP;

    CreateJavaVm(&vmP, &envP, vmArgsP); vm = new JavaVmWrapper(vmP); env = new JniEnvWrapper(envP); } var classPtr = env.FindClass("ru/kontur/Program"); var constructor = env.GetMethodId(classPtr, "<init>", "()V"); var runMethod = env.GetMethodId(classPtr, "run", "()V"); var obj = env.NewObject(classPtr, constructor, port); env.CallObjectMethod(obj, runMethod); C#-обѐртка вокруг JNI 54
  70. • classpath • ClassLoader URL[] jars = new URL[]{new URL("file:///c:/fop/jars/fop.jar")};

    URLClassLoader cl = new URLClassLoader(jars); Class<?> serviceClass = cl.loadClass("ru.kontur.fop.FopService"); Загрузка классов 55
  71. • classpath • ClassLoader URL[] jars = new URL[]{new URL("file:///c:/fop/jars/fop.jar")};

    URLClassLoader cl = new URLClassLoader(jars); Class<?> serviceClass = cl.loadClass("ru.kontur.fop.FopService"); Загрузка классов 55
  72. • classpath • ClassLoader URL[] jars = new URL[]{new URL("file:///c:/fop/jars/fop.jar")};

    URLClassLoader cl = new URLClassLoader(jars); Class<?> serviceClass = cl.loadClass("ru.kontur.fop.FopService"); Загрузка классов 55
  73. • classpath • ClassLoader URL[] jars = new URL[]{new URL("file:///c:/fop/jars/fop.jar")};

    URLClassLoader cl = new URLClassLoader(jars); Class<?> serviceClass = cl.loadClass("ru.kontur.fop.FopService"); Загрузка классов 55
  74. public void InjectClass(string className, byte[] classBytes) { var loaderClass =

    new JavaClassLoaderClass(env); var loader = loaderClass.GetSystemClassLoader(); var loadedClass = env.DefineClass( className, loader, classBytes, classBytes.Length); } Загрузка классов 56
  75. public void InjectClass(string className, byte[] classBytes) { var loaderClass =

    new JavaClassLoaderClass(env); var loader = loaderClass.GetSystemClassLoader(); var loadedClass = env.DefineClass( className, loader, classBytes, classBytes.Length); } Загрузка классов 56
  76. public void InjectClass(string className, byte[] classBytes) { var loaderClass =

    new JavaClassLoaderClass(env); var loader = loaderClass.GetSystemClassLoader(); var loadedClass = env.DefineClass( className, loader, classBytes, classBytes.Length); } Загрузка классов 56
  77. public void InjectClass(string className, byte[] classBytes) { var loaderClass =

    new JavaClassLoaderClass(env); var loader = loaderClass.GetSystemClassLoader(); var loadedClass = env.DefineClass( className, loader, classBytes, classBytes.Length); } Загрузка классов 56
  78. • Запустили виртуальную машину Java внутри .NET процесса • Реализовали

    обмен данными между программами на Java и на C# Промежуточные итоги 57
  79. • Запустили виртуальную машину Java внутри .NET процесса • Реализовали

    обмен данными между программами на Java и на C# • Получили готовый работающий прототип решения задачи конвертации документов Промежуточные итоги 57
  80. • Ручное управление объектами, размещѐнными в нативной памяти • Используем

    using-блоки (аналог try- with-resources) Указатели в нативную память 60
  81. • Объект существует на стороне Java • В нативный код

    передаѐтся по «локальной» ссылке Объекты из Java heap 61
  82. • Объект существует на стороне Java • В нативный код

    передаѐтся по «локальной» ссылке var wrappedMethodId = Env.GetMethodId(classPtr, "<init>", "(I)V"); /* ... */ Env.DeleteLocalRef(wrappedMethodId.LocalRef); Объекты из Java heap 61
  83. • Объект существует на стороне Java • В нативный код

    передаѐтся по «локальной» ссылке var wrappedMethodId = Env.GetMethodId(classPtr, "<init>", "(I)V"); /* ... */ Env.DeleteLocalRef(wrappedMethodId.LocalRef); Объекты из Java heap 61
  84. • Объект существует на стороне Java • В нативный код

    передаѐтся по «локальной» ссылке • Как Java GC узнает, что пора собирать? var wrappedMethodId = Env.GetMethodId(classPtr, "<init>", "(I)V"); /* ... */ Env.DeleteLocalRef(wrappedMethodId.LocalRef); Объекты из Java heap 61
  85. • Объект существует на стороне Java • В нативный код

    передаѐтся по «локальной» ссылке • Как Java GC узнает, что пора собирать? var wrappedMethodId = Env.GetMethodId(classPtr, "<init>", "(I)V"); /* ... */ Env.DeleteLocalRef(wrappedMethodId.LocalRef); Объекты из Java heap 61
  86. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); А что с многопоточностью? 62
  87. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); А что с многопоточностью? 62
  88. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); А что с многопоточностью? 62
  89. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); Env.MonitorEnter(obj); /* synchronized block */ Env.MonitorExit(obj); synchronized (obj) { /* synchronized block */ } А что с многопоточностью? 62
  90. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); Env.MonitorEnter(obj); /* synchronized block */ Env.MonitorExit(obj); synchronized (obj) { /* synchronized block */ } А что с многопоточностью? 62
  91. var env = Jvm.AttachCurrentThread(); /* process */ Jvm.DetachCurrentThread(); var globalRef

    = Env.NewGlobalRef(localRef); /* process */ Env.DeleteGlobalRef(globalRef); Env.MonitorEnter(obj); /* synchronized block */ Env.MonitorExit(obj); synchronized (obj) { /* synchronized block */ } А что с многопоточностью? 62
  92. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  93. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  94. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  95. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  96. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  97. Exception T SafeJniCall<T>(Func<T> func) { T result = func(); if

    (Env.ExceptionCheck()) { var exception = Env.ExceptionOccurred(); /* process exception */ Env.ExceptionClear(); } return result; } 63
  98. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  99. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  100. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  101. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  102. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  103. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  104. var enumClass = env.FindClass("ru/kontur/fop/TransformationType"); var fieldIdPDF = env.GetStaticFieldID(enumClass, "PDF", "Lru/kontur/fop/TransformationType;");

    var PDF = env.GetStaticObjectField(enumClass, fieldIdPDF); package ru.kontur.fop; public enum TransformationType { CSV, PDF, SVG; } Enum 64
  105. byte[] public byte[] convert(byte[] data) { // ... } using

    (var byteArray = Env.NewByteArray(bytes)) { Env.CallObjectMethod( obj, method, new JValue() { PointerValue = byteArray.LocalRef }; } 65
  106. byte[] public byte[] convert(byte[] data) { // ... } using

    (var byteArray = Env.NewByteArray(bytes)) { Env.CallObjectMethod( obj, method, new JValue() { PointerValue = byteArray.LocalRef }; } 65
  107. byte[] public byte[] convert(byte[] data) { // ... } using

    (var byteArray = Env.NewByteArray(bytes)) { Env.CallObjectMethod( obj, method, new JValue() { PointerValue = byteArray.LocalRef }; } 65
  108. byte[] public byte[] convert(byte[] data) { // ... } using

    (var byteArray = Env.NewByteArray(bytes)) { Env.CallObjectMethod( obj, method, new JValue() { PointerValue = byteArray.LocalRef }; } 65
  109. byte[] public byte[] convert(byte[] data) { // ... } using

    (var byteArray = Env.NewByteArray(bytes)) { Env.CallObjectMethod( obj, method, new JValue() { PointerValue = byteArray.LocalRef }; } 65
  110. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  111. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  112. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  113. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  114. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  115. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  116. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  117. DirectByteBuffer fixed (byte* b = bytes) { var buffer =

    Env.NewDirectByteBuffer((IntPtr)b, bytes.Length)); Env.CallIntMethod( obj, method, new JValue() { PointerValue = buffer.LocalRef }); } public int convert(ByteBuffer buffer) { byte[] data = new byte[buffer.capacity()]; buffer.get(data); // convert } 66
  118. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    67 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс j4net proxy ? ? ? gRPC 155,9 ± 2,463 мкс 495,4 ± 7,676 мкс 5 417,8 ± 526,193 мкс JSON 1,380 ± 0,040 мс 10,177 ± 0,117 мс 98,441 ± 1,820 мс
  119. Результаты бенчмарка • Алгоритм SHA-512 • Переменная длина массива байтов

    68 5 000 50 000 500 000 plain java 24,41 ± 0,365 мкс 230,87 ± 2,613 мкс 2 339,28 ± 49,665 мкс j4net proxy 29,23 ± 0,540 мкс 248,06 ± 3,032 мкс 2 486,54 ± 60,480 мкс gRPC 155,9 ± 2,463 мкс 495,4 ± 7,676 мкс 5 417,8 ± 526,193 мкс JSON 1,380 ± 0,040 мс 10,177 ± 0,117 мс 98,441 ± 1,820 мс
  120. • Десктоп – всѐ в одном процессе (удобно) • Большой

    memory traffic (быстро) Где может пригодиться? 69
  121. • Десктоп – всѐ в одном процессе (удобно) • Большой

    memory traffic (быстро) • Не нужна дополнительная инфраструктура для работы (просто и надѐжно) Где может пригодиться? 69
  122. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  123. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  124. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  125. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  126. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  127. Java user lib 76 public class Tourist { public static

    Answer ask(Question question) { /* ... */ } public void walk(byte[] map) { /* ... */ } }
  128. Java user lib proxy • C#-proxy для Java-библиотеки • Подключается

    к C#-проекту как обычная .NET-библиотека 78
  129. • Примеры на Github: https://github.com/gnkoshelev • j4net: https://github.com/j4net (прототип, J4Net.Core

    использовался в примерах) • gRPC http://www.grpc.io/ • JNI http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html • Invocation API http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html Ссылки 84