أهلاً! أعتقد أنك لن تتفاجأ كثيرًا إذا أخبرتك أن جهاز الكمبيوتر الخاص بك يحتوي على كمية محدودة من الذاكرة :)
حتى محرك الأقراص الثابتة لديك (الذي يبلغ حجمه أضعاف ذاكرة الوصول العشوائي) يمكن أن يمتلئ بألعابك المفضلة وبرامجك التلفزيونية وأشياء أخرى. لمنع حدوث ذلك، تحتاج إلى مراقبة الحالة الحالية لذاكرة جهاز الكمبيوتر الخاص بك وحذف الملفات غير الضرورية. كيف يرتبط كل هذا ببرمجة جافا؟ بشكل مباشر تمامًا! بعد كل شيء، يؤدي إنشاء أي كائن إلى قيام جهاز Java بتخصيص ذاكرة له . يقوم برنامج كبير في العالم الحقيقي بإنشاء عشرات أو مئات الآلاف من الكائنات، ويتم تخصيص جزء من الذاكرة لكل منها. ولكن ما رأيك، كم عدد هذه الكائنات الموجودة؟ هل هم "أحياء" طوال فترة تشغيل برنامجنا؟ بالطبع لا. حتى مع كل مزاياها، فإن كائنات Java ليست خالدة :) الكائنات لها دورة حياتها الخاصة. سنأخذ اليوم استراحة قصيرة من كتابة التعليمات البرمجية ونستكشف هذه العملية :) كما أنها مهمة جدًا لفهم كيفية عمل البرنامج وإدارة الموارد. إذًا، أين تبدأ حياة الكائن؟ مثل الإنسان منذ ولادته، أي عند خلقه.
جامع البيانات المهملة هو آلية Java داخلية مسؤولة عن تحرير الذاكرة، أي إزالة الكائنات غير الضرورية من الذاكرة. هناك سبب وراء اختيارنا لتمثيلها باستخدام مكنسة كهربائية روبوتية. يعمل جامع البيانات المهملة بنفس الطريقة تقريبًا: فهو "يتحرك" في برنامجك في الخلفية، ويجمع البيانات المهملة. ليس عليك عمليا التفاعل معها. وتتمثل مهمتها في حذف الكائنات التي لم تعد مستخدمة في البرنامج. وبالتالي، فإنه يحرر الذاكرة للأشياء الأخرى. هل تتذكر أنه في بداية الدرس قلنا أنه في الحياة الواقعية عليك مراقبة حالة جهاز الكمبيوتر الخاص بك وحذف الملفات القديمة؟ إذا كنا نتحدث عن كائنات Java، فإن أداة تجميع البيانات المهملة تقوم بذلك نيابةً عنك . يتم تشغيل أداة تجميع البيانات المهملة عدة مرات أثناء تشغيل برنامجك: لا يلزمك الاتصال بها بشكل صريح وإعطائها الأوامر (على الرغم من أن هذا ممكن من الناحية التقنية). سنتحدث أكثر عن أداة تجميع البيانات المهملة لاحقًا ونحلل كيفية عملها بمزيد من التفصيل. عندما يصل مجمع البيانات المهملة إلى كائن ما، قبل تدميره مباشرة،
لكي تعمل المكنسة الكهربائية بشكل صحيح، تحتاج إلى الحفاظ على الأرضية في حالة جيدة والتقاط كل ما لا تستطيع التعامل معه. يتبع جامع القمامة نفس المبدأ. إذا كان البرنامج يحتوي على الكثير من الكائنات التي لا يمكنه تنظيفها (مثل جورب أو قطع الليغو لمكنستنا الكهربائية الآلية)، فسوف تنفد الذاكرة في يوم من الأيام. لن يقتصر الأمر على توقف برنامجك فحسب، بل ستتوقف أيضًا جميع البرامج الأخرى التي تعمل على الكمبيوتر. بعد كل شيء، لن يكون لديهم ذاكرة كافية أيضًا (بالعودة إلى تشبيهنا، الزجاج المكسور على الأرض لا يوقف المكنسة الكهربائية فحسب، بل يوقف أيضًا الأشخاص الذين يعيشون في المنزل). باختصار، هذا هو ما تبدو عليه دورة حياة الكائن وجمع البيانات المهملة في Java. لا تحتاج إلى حفظ هذا: يكفي أن تفهم ببساطة كيف يعمل. وفي الدرس التالي، سنعود إلى هذه العمليات بمزيد من التفصيل. لكن في الوقت الحالي، يمكنك العودة إلى حل مهام CodeGym :) حظًا موفقًا!
Cat cat = new Cat();// Our Cat object's lifecycle begins now!
أولاً، يقوم جهاز Java الظاهري بتخصيص الذاكرة اللازمة لإنشاء الكائن. ثم يقوم بإنشاء مرجع له (في حالتنا، cat
) لتمكين تتبعه. بعد ذلك، تتم تهيئة جميع المتغيرات، ويتم استدعاء المُنشئ، ويعيش كائننا الجديد الآن حياته الخاصة :) يختلف عمر الكائنات. لا توجد أرقام دقيقة هنا. على أية حال، الكائن يعيش في البرنامج ويؤدي وظائفه لفترة معينة من الزمن. على وجه الدقة، الكائن "حي" طالما أن هناك إشارات إليه. بمجرد عدم وجود مراجع، "يموت" الكائن. على سبيل المثال:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
lamborghini = null;
}
}
في هذه main()
الطريقة، يتوقف كائن سيارة "Lamborghini Diablo" عن الحياة في السطر الثاني. كان هناك مرجع واحد فقط إليه، وتم تعيين المرجع على قيمة خالية. نظرًا لعدم وجود أي إشارات متبقية إلى Diablo، فقد أصبحت "قمامة". ليس من الضروري تعيين المرجع على الصفر حتى يحدث ذلك:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
lamborghini = lamborghiniGallardo;
}
}
لقد أنشأنا هنا كائنًا ثانيًا وقمنا بتعيينه لمرجع لامبورغيني. الآن يشير مرجعان إلى Lamborghini Gallardo
الكائن، لكن Lamborghini Diablo
الكائن لا يحتوي على أي شيء. هذا يعني أن Diablo
الكائن يصبح قمامة. يحدث هذا عند بدء تشغيل أداة تجميع البيانات المهملة (GC) المدمجة في Java .
finalize()
يتم استدعاء الطريقة الخاصة للكائن. يمكن استدعاء هذه الطريقة لتحرير بعض الموارد الإضافية التي يستخدمها الكائن. تنتمي الطريقة finalize()
إلى فئة الكائن. بمعنى آخر، إنه مشابه لـ equals()
و hashCode()
( toString()
الذي التقيت به سابقًا). كل كائن لديه . وهو يختلف عن الطرق الأخرى في ذلك...كيف ينبغي لنا أن نقول هذا...إنه أمر متعمد للغاية. ونعني بذلك أنه لا يتم استدعاؤه دائمًا قبل تدمير الكائن . البرمجة نشاط دقيق للغاية. يخبر المبرمج الكمبيوتر أن يفعل شيئًا ما، والكمبيوتر يفعل ذلك. أفترض أنك اعتدت على هذا النوع من السلوك، لذلك قد يكون من الصعب عليك في البداية قبول الفكرة التالية: "قبل تدمير الكائن، يتم استدعاء طريقة فئة الكائن. أو لا. إذا حالفنا الحظ finalize()
! " ومع ذلك، هذا هو الواقع. يحدد جهاز Java نفسه ما إذا كان سيتم استدعاء Finalize() على أساس كل حالة على حدة. على سبيل التجربة، دعونا نحاول تشغيل الكود التالي:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public Cat() {
}
public static void main(String[] args) throws Throwable {
for (int i = 0 ; i < 1000000; i++) {
Cat cat = new Cat();
cat = null;// The first object becomes available for garbage collection here
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("The Cat is destroyed!");
}
}
نقوم بإنشاء Cat
كائن، وفي السطر التالي نقوم بإلغاء الإشارة الوحيدة إليه. ونحن نفعل ذلك مليون مرة. لقد تجاوزنا هذه finalize()
الطريقة بشكل صريح. في كل مرة Cat
يتم فيها تدمير كائن، يجب أن يعرض سلسلة - ما مجموعه مليون مرة. لكن لا! على وجه الدقة، تم تنفيذه على جهاز الكمبيوتر الخاص بي 37346 مرة فقط! بمعنى آخر، قررت آلة Java الخاصة بي استدعاء finalize()
الطريقة في حالة واحدة فقط من كل 27 حالة. وفي الحالات الأخرى، لم تتضمن عملية جمع البيانات المهملة هذه المكالمة. حاول تشغيل هذا الرمز بنفسك. على الأرجح سوف تحصل على نتيجة مختلفة. كما ترون، من الصعب الاتصال finalize()
بشريك موثوق به :) لذا، إليك نصيحة صغيرة للمستقبل: لا تعتمد على finalize()
طريقة تحرير الموارد المهمة. قد يستدعيه JVM، وقد لا يفعل ذلك. من تعرف؟ إذا كان الكائن الخاص بك يحتوي على بعض الموارد الهامة للأداء (على سبيل المثال، اتصال قاعدة بيانات مفتوحة) أثناء وجوده، فسيكون من الأفضل إنشاء طريقة خاصة واستدعاءها بشكل صريح لتحريرها عندما لا تكون هناك حاجة للكائن. وبهذه الطريقة، ستعرف على وجه اليقين أن أداء برنامجك لن يتأثر. لقد بدأنا بالقول إن العمل مع الذاكرة وجمع البيانات المهملة موضوعان مهمان للغاية، وهما كذلك بالفعل. يمكن أن يؤدي سوء التعامل مع الموارد وسوء فهم كيفية تنظيف الكائنات غير الضرورية إلى أحد أكثر الأخطاء المزعجة: تسرب الذاكرة . وهذا من أشهر الأخطاء البرمجية. حتى أنه يحتوي على مقالة
ويكيبيديا خاصة به . يمكن أن تؤدي التعليمات البرمجية المكتوبة بشكل سيء إلى إنشاء موقف حيث يتم تخصيص الذاكرة في كل مرة للكائنات التي تم إنشاؤها حديثًا، ولكن الكائنات القديمة غير الضرورية غير متاحة لجمع البيانات المهملة. نظرًا لأننا قمنا بالفعل بعمل تشبيه للمكنسة الكهربائية الروبوتية، تخيل ماذا سيحدث إذا قمت قبل تشغيل الروبوت بتوزيع الجوارب في جميع أنحاء المنزل، وحطمت مزهرية زجاجية، وتركت قطع الليغو في جميع أنحاء الأرض. من الطبيعي أن يحاول الروبوت القيام بشيء ما، لكنه سيتوقف في يوم من الأيام.
GO TO FULL VERSION