Bosh sahifa Wiki JNI

JNI

JNIJava Virtual Machine ichidagi Java yoki Kotlin kodi bilan C va C++ kabi native kod orasida aloqa o‘rnatadigan standart interfeys. To‘liq nomi Java Native Interface. Androidda JNI NDK orqali build qilingan native kutubxonalarni Kotlin yoki Java application qismi bilan bog‘lashda keng ishlatiladi.

JNI method chaqirish, object yaratish, field o‘qish, array bilan ishlash va exception uzatish imkonini beradi. Biroq u type, memory va thread lifecycle’ini ehtiyotkor boshqarishni talab qiladi.

Native method

Java yoki Kotlin class native method e’lon qiladi:

external fun process(data: ByteArray): Int

Bu methodning implementationi JVM bytecode ichida emas, native library’da joylashadi.

Library oldindan System.loadLibrary bilan yuklanadi.

Nom bo‘yicha bog‘lash

Native function maxsus nomlash qoidasi bilan methodga ulanadi:

Java_package_Class_method

Package va class nomlari encoded qilinadi.

Overloaded methodlar signature suffix talab qilishi mumkin.

Uzun automatic nomlar sabab explicit registration ko‘p loyihada qulayroq.

RegisterNatives

Native methodlarni runtime’da jadval orqali ro‘yxatdan o‘tkazish mumkin.

Jadval:

ni saqlaydi.

Bu symbol nomini soddalashtiradi va obfuscation bilan ishlashni nazorat qilishga yordam beradi.

JNIEnv

Har native JNI function JNIEnv* pointer oladi.

U JVM operationlarini chaqirish uchun function table vazifasini bajaradi.

JNIEnv ayni threadga tegishli.

Uni boshqa threadga saqlab uzatish mumkin emas.

JavaVM

JavaVM* processdagi virtual machine’ni ifodalaydi.

U global saqlanishi mumkin.

Native thread JVM bilan ishlashi kerak bo‘lsa JavaVM orqali attach qilinadi va JNIEnv olinadi.

Thread tugaganda detach qilinadi.

Local reference

JNI method ichida olingan object reference odatda local reference hisoblanadi.

U native call tugaganda avtomatik bo‘shatiladi.

Loop ichida minglab local reference yaratilsa jadval to‘lishi mumkin.

DeleteLocalRef yoki local frame bilan erta tozalanadi.

Global reference

Object native calllar orasida saqlanishi kerak bo‘lsa global reference yaratiladi.

Garbage collector global reference mavjud objectni o‘chirmaydi.

Kerak bo‘lmaganda DeleteGlobalRef chaqirilmasa memory leak yuz beradi.

Weak global reference GC’ga objectni tozalash imkonini beradi.

Class topish

FindClass class nomi orqali reference qaytaradi.

Native method JVM tomonidan chaqirilgan threadda class loader contexti mos bo‘lishi mumkin.

Native yaratilgan thread’da application classini topish qiyinlashadi.

Kerakli class yoki class loader oldindan global reference sifatida saqlanishi mumkin.

Method ID

Method chaqirish uchun avval jmethodID olinadi.

Bunda:

aniq bo‘lishi kerak.

Signature xato bo‘lsa method topilmaydi va exception paydo bo‘lishi mumkin.

Method ID class lifecycle’iga bog‘liq.

Type signature

JNI primitive va object turlarni compact belgilar bilan ifodalaydi.

Masalan:

  • I — int;
  • J — long;
  • Z — boolean;
  • V — void;
  • Ljava/lang/String;object;
  • [ — array.

Method signature parameterlar qavs ichida, keyin return type bilan yoziladi.

String

Java string UTF-16 representationga yaqin ishlaydi.

JNI UTF charlar uchun maxsus functionlar beradi.

“Modified UTF-8” oddiy standart UTF-8 bilan aynan bir xil emas.

Unicode data conversionida uzunlik, null byte va surrogate juftlar hisobga olinadi.

Array

Primitive array elementlariga:

orqali murojaat qilish mumkin.

GetByteArrayElements har doim original memory pointerini qaytarishi kafolatlanmaydi.

Release mode o‘zgarishlarni qaytarish yoki bekor qilishni belgilaydi.

DirectByteBuffer

Native memory Java tomonda direct buffer sifatida ko‘rinishi mumkin.

Bu katta data nusxasini kamaytiradi.

Buffer capacity va native memory lifecycle’i aniq boshqariladi.

Java buffer mavjud paytda native memory bo‘shatilsa use-after-free yuz beradi.

Exception

JNI operation exception yaratsa native kod uni tekshirishi kerak.

Pending exception mavjud bo‘lsa ko‘p JNI functionlarni chaqirish mumkin emas.

Native kod:

  • ExceptionCheck;
  • ExceptionDescribe;
  • ExceptionClear;
  • ThrowNew

kabi operationlardan foydalanadi.

Exceptionni sababsiz clear qilish xatoni yashiradi.

Native exception

C++ exception JVM boundary’dan tashqariga chiqmasligi kerak.

Boundary’da try-catch bilan ushlanadi.

Keyin Java exceptionga map qilinadi.

Aks holda process terminate bo‘lishi mumkin.

Thread attach

JVM yaratmagan native thread JNI’dan foydalanishdan oldin attach qilinadi.

Uzoq yashaydigan thread bir marta attach, tugaganda detach qilinadi.

Har chaqiriqda noto‘g‘ri attach/detach qilish overhead va leak yaratishi mumkin.

Androidda thread local destructorga oid usullar ishlatilishi mumkin.

Synchronization

JNI Java monitorini lock qilish imkonini beradi.

Native mutex va Java monitorini aralashtirish lock order muammosiga olib kelishi mumkin.

Deadlockdan qochish uchun boundary ichida uzun blocking operation bajarilmaydi.

Performance

JNI boundary callning o‘zi ma’lum overheadga ega.

Shu sababli:

  • ko‘p mayda call o‘rniga batch;
  • data copy’ni kamaytirish;
  • method ID cache;
  • class lookupni takrorlamaslik;
  • primitive array

ishlatiladi.

Performance o‘lchov bilan tekshiriladi.

Obfuscation

Code shrinker Java class yoki method nomini o‘zgartirishi mumkin.

Native kod string orqali eski nomni qidirsagina runtime xato yuz beradi.

Keep rule yoki RegisterNatives uchun stable entry point ishlatiladi.

Generated binding obfuscation mapping bilan mos bo‘lishi kerak.

Security

JNI native kodga JVM himoyasidan tashqari memory access beradi.

Length, pointer, object type va thread holati tekshiriladi.

Untrusted inputni native parserga uzatishdan oldin format va limitlar belgilanadi.

Native kutubxona signing va supply-chain nazorati bilan olinadi.

Bog‘liq tushunchalar

Java Native Interface, JNI, JNIEnv, JavaVM, Native method, Local reference, Global reference, NDK, JVM, DirectByteBuffer, Native library