سؤال

عندما أقوم بإنشاء كائن Java باستخدام أساليب JNI، من أجل تمريره كمعلمة إلى طريقة Java التي أستدعيها باستخدام واجهة برمجة تطبيقات استدعاء JNI، كيف يمكنني إدارة ذاكرته؟

إليك ما أعمل معه:

لدي كائن C يحتوي على طريقة مدمرة أكثر تعقيدًا من ذلك free().يجب أن يرتبط كائن C هذا بكائن Java، وبمجرد الانتهاء من التطبيق باستخدام كائن Java، لم أعد بحاجة إلى كائن C.

أقوم بإنشاء كائن Java على هذا النحو (تم حذف التحقق من الأخطاء من أجل الوضوح):

c_object = c_object_create ();
class = (*env)->FindClass (env, "my.class.name");
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V");
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object);

method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V");
(*env)->CallVoidMethod (env, other_class, method, instance);

والآن بعد أن انتهيت من ذلك instance, ، ماذا افعل به؟من الناحية المثالية، أود أن أترك مهمة جمع البيانات المهملة إلى الجهاز الافتراضي؛عند الانتهاء من ذلك instance سيكون أمرا رائعا إذا دعا أيضا c_object_destroy() على المؤشر الذي قدمته له.هل هذا ممكن؟

هناك سؤال منفصل ولكنه ذو صلة يتعلق بنطاق كيانات Java التي أقوم بإنشائها بطريقة مثل هذه؛هل يجب علي أن أحرر يدويًا، على سبيل المثال، class, constructor, ، أو method فوق؟مستند JNI غامض بشكل محبط (في رأيي) فيما يتعلق بموضوع الإدارة السليمة للذاكرة.

هل كانت مفيدة؟

المحلول

هناك استراتيجيتان لاستعادة الموارد الأصلية (الكائنات، واصفات الملفات، وما إلى ذلك)

  1. قم باستدعاء طريقة JNI أثناء وضع اللمسات النهائية () والتي تحرر المورد.بعض الناس يوصي بعدم تنفيذ اللمسات النهائية, ، وفي الأساس لا يمكنك التأكد من تحرير موردك الأصلي على الإطلاق.بالنسبة للموارد مثل الذاكرة، ربما لا تكون هذه مشكلة، ولكن إذا كان لديك ملف على سبيل المثال يحتاج إلى مسحه في وقت يمكن التنبؤ به، فمن المحتمل أن لا تكون الصيغة النهائية () فكرة جيدة.

  2. استدعاء طريقة التنظيف يدويًا.يعد هذا مفيدًا إذا كان لديك وقت تعرف فيه أنه يجب تنظيف المورد.لقد استخدمت هذه الطريقة عندما كان لدي مورد يجب إلغاء تخصيصه قبل إلغاء تحميل DLL في كود JNI.من أجل السماح بإعادة تحميل DLL لاحقًا، كان علي أن أتأكد من إلغاء تخصيص الكائن بالفعل قبل محاولة إلغاء تحميل DLL.باستخدام Finalize() فقط، لم أكن لأحصل على هذا المضمون.يمكن دمج هذا مع (1) للسماح بتخصيص المورد إما أثناء وضع اللمسات النهائية () أو بطريقة التنظيف التي تسمى يدويًا.(ربما تحتاج إلى خريطة أساسية لـ WeakReferences لتتبع الكائنات التي تحتاج إلى استدعاء طريقة التنظيف الخاصة بها.)

  3. من المفترض أن مرجع فانتوم يمكن استخدامها لحل هذه المشكلة أيضًا، لكنني لست متأكدًا تمامًا من كيفية عمل هذا الحل.

في الواقع، لا بد لي من أن أختلف معك بشأن وثائق JNI.أجد مواصفات جي ان اي واضح بشكل استثنائي في معظم القضايا المهمة، حتى لو كان من الممكن أن تكون الأقسام الخاصة بإدارة المراجع المحلية والعالمية أكثر تفصيلاً.

نصائح أخرى

تغطي مواصفات JNI مسألة من "يمتلك" كائنات Java التي تم إنشاؤها بطرق JNI هنا.تحتاج إلى التمييز بين محلي و عالمي مراجع.

عندما يقوم JVM باستدعاء JNI إلى التعليمات البرمجية الأصلية، فإنه يقوم بإعداد سجل لتتبع جميع الكائنات التي تم إنشاؤها أثناء المكالمة.أي كائن تم إنشاؤه أثناء المكالمة الأصلية (أيتم إرجاعها من وظيفة واجهة JNI) إلى هذا السجل.تُعرف الإشارات إلى مثل هذه الكائنات باسم المراجع المحلية.عندما تعود الطريقة الأصلية إلى JVM، يتم تدمير كافة المراجع المحلية التي تم إنشاؤها أثناء استدعاء الطريقة الأصلية.إذا كنت تقوم بإجراء مكالمات مرة أخرى إلى JVM أثناء استدعاء الطريقة الأصلية، فسيظل المرجع المحلي نشطًا عندما يعود التحكم إلى الطريقة الأصلية.إذا قام JVM الذي تم استدعاؤه من التعليمات البرمجية الأصلية بإجراء اتصال آخر بالكود الأصلي، فسيتم إنشاء سجل جديد للمراجع المحلية، ويتم تطبيق نفس القواعد.

(في الواقع، يمكنك تنفيذ JVM الخاص بك القابل للتنفيذ (أي.java.exe) باستخدام واجهة JNI، عن طريق إنشاء JVM (وبالتالي تلقي مؤشر JNIEnv *)، والبحث عن الفئة المعطاة في سطر الأوامر، واستدعاء الطريقة main() عليها.)

جميع المراجع التي يتم إرجاعها من طرق واجهة JNI محلية.هذا يعني أنه في ظل الظروف العادية، لا تحتاج إلى إلغاء تخصيص المراجع التي يتم إرجاعها يدويًا بواسطة طرق JNI، حيث يتم تدميرها عند العودة إلى JVM.في بعض الأحيان لا تزال ترغب في تدميرها "قبل الأوان"، على سبيل المثال عندما يكون لديك الكثير من المراجع المحلية التي تريد حذفها قبل العودة إلى JVM.

يتم إنشاء المراجع العمومية (من المراجع المحلية) باستخدام NewGlobalRef().تتم إضافتها إلى سجل خاص ويجب إلغاء تخصيصها يدويًا.تُستخدم المراجع العامة فقط لكائن Java الذي يحتاج الكود الأصلي إلى الاحتفاظ بمرجع إليه عبر استدعاءات JNI المتعددة، على سبيل المثال، إذا كان لديك كود أصلي يؤدي إلى أحداث يجب نشرها مرة أخرى إلى Java.في هذه الحالة، يحتاج كود JNI إلى تخزين مرجع لكائن Java الذي سيستقبل الحدث.

نأمل أن يوضح هذا مشكلة إدارة الذاكرة قليلاً.

ورد: "سؤال منفصل، ولكن ذات الصلة" ... لا تحتاج لاطلاق سراح jclass، jfieldID وjmethodID يدويا عند استخدامها في سياق "المحلية". ينبغي الإفراج عن أي مراجع الكائنات الفعلية الحصول على (وليس jclass، jfieldID، jmethodID) مع DeleteLocalRef.

ووGC أن جمع سبيل المثال، ولكنه لن تطلق سراح تلقائيا كومة الذاكرة غير جافا المخصصة في التعليمات البرمجية الأصلية. يجب أن يكون أسلوب صريح في صفك للافراج عن المثال c_object.

وهذا هو واحد من الحالات حيث كنت توصي باستخدام finalizer التحقق إذا لقد تم الافراج عن c_object والافراج عنها، تسجيل رسالة إذا لم تكن.

وهناك تقنية مفيدة في إنشاء مثيل قابل للظهور في فئة منشئ جافا وتخزينه في حقل (أو مجرد تهيئة مضمنة الميدان). إذا بالكشف عن finalizer أن الطبقة لم يتم التخلص منها بشكل صحيح فإنه طباعة تتبع مكدس الذاكرة المؤقتة، إبراز كومة التخصيص.

واقتراح هو تجنب القيام JNI التوالي وتذهب مع gluegen أو <وأ href = " http://www.swig.org/ "يختلط =" نوفولو noreferrer "> جرعة كبيرة (وكلا توليد رمز ويمكن أن تكون مرتبطة بشكل ثابت).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top