عندما ينشئ البرنامج كيانات من النوع المرجعي مثل الكائنات والصفائف وما إلى ذلك ، سيقوم النظام بتخصيص قطعة من الذاكرة للكائن في ذاكرة الكومة ، ويتم تخزين الكائن في هذه الذاكرة. عندما لا يتم الإشارة إلى هذه القطعة من خلال أي متغير مرجعي ، تصبح قطعة الذاكرة قمامة ، في انتظار إعادة تدوير آلية جمع القمامة. آلية جمع القمامة لها ثلاث خصائص:
آلية جمع القمامة هي المسؤولة فقط عن إعادة تدوير الكائنات في ذاكرة الكومة ، ولن تقوم بإعادة تدوير أي موارد فعلية (مثل اتصالات قاعدة البيانات ، وموارد الملفات المفتوحة ، وما إلى ذلك) ، ولن تقوم بإعادة تدوير الذاكرة المخصصة للكائن بطريقة أخرى غير إنشاء كائن ، (مثل الذاكرة المطبقة على الكائن في Malloc في الطريقة المحلية)
لا يمكن للبرنامج التحكم بدقة في تشغيل جمع القمامة ، لذلك لا يمكن التوصية به إلا لجمع القمامة. هناك طريقتان موصىتان: System.gc () و Runtime.getRuntime (). GC ()
قبل جمع القمامة ، سيتم دائمًا استدعاء طريقة النهائيات () أولاً ، ولكنها هي نفس وقت جمع القمامة ، كما أن طريقة النهائيات () ليست متأكدة أيضًا.
فيما يتعلق بالخصائص الثلاث المذكورة أعلاه ، هناك ثلاث مشاكل:
1. يجب القيام بعمل التنظيف يدويًا لتحرير الذاكرة والموارد المادية الأخرى المخصصة بطريقة أخرى غير طريقة إنشاء الكائنات. وكن حذرًا من القضاء على مراجع الكائن المنتهية الصلاحية ، وإلا فقد يحدث OOM.
عادةً ما يستخدم التنظيف اليدوي بنية رمز مثل المحاولة ... أخيرًا ...
الأمثلة على النحو التالي:
استيراد java.io.fileInputStream ؛ استيراد java.io.filenotfoundException ؛ استيراد java.io.ioException ؛ الطبقة العامة manualclear {public static void main (string [] args) {fileInputStream fileInputStream = null ؛ حاول {fileInputStream = جديد fileInputStream ("./ src/manualclear.java") ؛ } catch (fileNotFoundException e) {system.out.println (e.getMessage ()) ؛ E.PrintStackTrace () ؛ يعود؛ } جرب {byte [] bbuf = new byte [1024] ؛ int hasread = 0 ؛ حاول {بينما ((hasread = fileInputStream.Read (bbuf))> 0) {system.out.println (سلسلة جديدة (bbuf ، 0 ، hasread)) ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ }} أخيرًا {try {fileInputStream.close () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }}}}عادة ما يكون هناك ثلاث حالات شائعة من OOM الناتجة عن الكائنات المنتهية الصلاحية. هذه الحالات الثلاث عادة لا يسهل اكتشافها ، ولن تكون هناك مشاكل في العمل في فترة زمنية قصيرة. ومع ذلك ، بعد فترة طويلة ، سيؤدي عدد الكائنات التي تم تسريبها في النهاية إلى تعطل البرنامج.
عندما يدير الفصل الذاكرة بمفرده ، يجب أن تكون حذراً من تسرب الذاكرة على النحو التالي:
استيراد java.util.arrays ؛ استيراد java.util.emptystackexception ؛ stack {{elements private [] ؛ حجم الباحث الخاص ؛ int static int default_inital_capacity = 16 ؛ public stack () {elements = new Object [default_inital_capacity] ؛ } public void push (object e) {insureCapacity () ؛ عناصر [size ++] = e ؛ } الكائن العام pop () {if (size == 0) {رمي جديد leghstackexception () ؛ } عناصر الإرجاع [-الحجم] ؛ } private void insureCapacity () {if (elements.length == size) {elements = arrays.copyof (elements ، 2 * size + 1) ؛ }}} الفئة العامة stackDemo {public static void main (string [] args) {stack stack = new stack () ؛ لـ (int i = 0 ؛ i <10000 ؛ i ++) {stack.push (new Object ()) ؛ } لـ (int i = 0 ؛ i <10000 ؛ i ++) {stack.pop () ؛ }}}سبب تسرب الذاكرة هو أنه حتى إذا لم تعد الكائنات الأخرى الموجودة على المكدس ، فإن العناصر [] في فئة المكدس لا تزال تشير إلى هذه الكائنات ، مما يؤدي إلى عدم إعادة تدوير الكائنات بواسطة جمع القمامة. لذلك ، عندما يحتاج الفصل إلى إدارة الذاكرة بمفرده ، احذر ما إذا كانت هذه المراجع المنتهية الصلاحية التي تحتفظ بها داخليًا تنبعث منها في الوقت المناسب. في هذا المثال ، فقط بعد إصدار المكدس ، سيتم عرض العرض المعروض.
عناصر [الحجم] = فارغة ؛
ذاكرة التخزين المؤقت يجب أن تكون حذرة من تسرب الذاكرة. عادةً ما يكون هذا الموقف هو أنه بمجرد وضع الكائن في ذاكرة التخزين المؤقت ، فمن المحتمل أن يكون من السهل نسيان ما إذا لم يتم استخدامه لفترة طويلة. عادة ، يمكن استخدام WakehashMap لتمثيل ذاكرة التخزين المؤقت. بعد انتهاء صلاحية العناصر الموجودة في ذاكرة التخزين المؤقت ، يمكن حذفها تلقائيًا. أو يمكن تنفيذها بشكل دوري بواسطة مؤشر ترابط الخلفية لمسح عناصر منتهية الصلاحية في المخزن المؤقت.
من الأفضل عرض تسجيل المستمعين أو عمليات الاسترجاعات إلى UNGISTER.
2. لا تستدعي اللمسات الأخيرة () يدويًا ، يتم استدعاؤها إلى جامع القمامة
3. تجنب استخدام طريقة اللمسات الأخيرة () ما لم يتم استخدامها كشرط النهاية لتجد أنه لم يتم تنظيف الكائن بشكل صحيح ؛ يتم استخدامه كشبكة أمان لتنظيف موارد النظام عند تنظيفها يدويًا ، نسيت المكالمات. لا ينبغي تنظيف التنظيف المتأخر. إذا قمت بتسجيل المعلومات حول مورد التنظيف المنسي في نفس الوقت ، فمن المناسب أيضًا اكتشاف الأخطاء لاحقًا وتعديل رمز التنظيف المنسي في الوقت المناسب ؛ تحرير موارد النظام غير الحرجة التي حصلت عليها الطريقة المحلية في الكائن.
نظرًا لأن طريقة النهائيات () لم يتم ضمانها بدقة ، فمن الأفضل عدم إطلاق الموارد الرئيسية ، ولكن يمكن استخدامها في الحالات الثلاث المذكورة أعلاه. الحالة الأولى هي كما يلي:
كتاب الفئة {boolean checkout = false ؛ الكتاب العام (oolean checkout) {this.checkout = checkout ؛ } public void checkin () {checkout = false ؛ } override void finize () يلقي رمي {if (checkout) {system.out.println ("error: check out") ؛ }}} الفئة العامة finalizeCheckObjectuse {public static void main (string [] args) {new Book (true) ؛ System.gc () ؛ }}نتائج التنفيذ:
خطأ: تحقق من
يجب أن يكون كائن الكتاب في المثال في حالة الشيكات قبل إصداره ، وإلا لا يمكن إصداره. يمكن أن يساعد التنفيذ في اللمسات الأخيرة على اكتشاف الأشياء غير القانونية في الوقت المناسب ، أو بشكل مباشر ، استخدام متغير مرجعي للإشارة إليه مباشرةً في النهاية ، بحيث يمكنه إعادة إدخال حالة الوصول إليها ، ثم معالجتها مرة أخرى.
هناك نقطة أخرى نلاحظها هي أنه إذا تجاوزت الفئة الفرعية طريقة الانتهاء من فئة الوالدين ، لكنها تنسى الاتصال يدويًا Super.Finlize أو أن عملية النهائيات للكاسفة الفرعية لها استثناء ، مما يؤدي إلى عدم تنفيذ الفئة الفائقة.
على النحو التالي:
الفئة Parent {Override المحمية void finize () رمي {system.out.println (getClass (). getName () + "الانتهاء من البدء") ؛ }} Class Son يمتد Parent {Override المحمية void finize () رمي {system.out.println (getClass (). getName () + "Finize Start") ؛ }} الفئة العامة superfinizelost {public static void main (string [] args) {new son () ؛ System.gc () ؛ }}نتائج التشغيل:
الابن الانتهاء من البدء
أو
الفئة Parent {Override المحمية void finize () رمي {system.out.println (getClass (). getName () + "الانتهاء من البدء") ؛ }} Class Son يمتد Parent {Override المحمية void finize () رمي {system.out.println (getClass (). getName () + "Finize Start") ؛ int i = 5/0 ؛ super.finalize () ؛ }} الفئة العامة superfinizelost {public static void main (string [] args) {new son () ؛ System.gc () ؛ }}نتائج التنفيذ:
الابن الانتهاء من البدء
بالنسبة للحالة الثانية ، يمكنك استخدام المحاولة ... أخيرًا ... بنية لحلها ، ولكن بالنسبة للحالة الأولى ، من الأفضل استخدام طريقة تسمى Guardian End Method. المثال كما يلي
Class Parent2 {private Final Object FinizeGuardian = new Object () {protected void finize () remable {system.out.println ("تنفيذ المنطق في طريقة إنهاء الفئة الأصل هنا") ؛ } ؛ } ؛} class son2 يمتد parent2 {Override void finize () رمي {system.out.println (getClass (). getName () + "الانتهاء من البدء") ؛ int i = 5/0 ؛ super.finalize () ؛ }} الفئة العامة FinizeGuardian {public static void main (string [] args) {new son2 () ؛ System.gc () ؛ }}نتائج التنفيذ:
قم بتنفيذ المنطق في طريقة إنهاء الفئة الأصل هنا
SON2 الانتهاء من البداية
هذا يضمن تنفيذ العمليات المطلوبة في الطريقة النهائية لفئة الأصل.
ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون مفيدًا لتعلم الجميع.