1. نموذج ذاكرة جافا
عند تنفيذ برنامج ما ، يقسم الجهاز الظاهري Java الذاكرة التي يديرها إلى العديد من مناطق البيانات. يظهر توزيع مناطق البيانات هذه في الشكل أدناه:
عداد البرنامج: منطقة ذاكرة صغيرة تشير إلى رمز bytecode الذي تم تنفيذه حاليًا. إذا كان مؤشر الترابط ينفذ طريقة Java ، فإن هذا العداد يسجل عنوان تعليمات الجهاز الظاهري Bytecode الذي يتم تنفيذه. إذا تم تنفيذ الطريقة الأصلية ، تكون قيمة الحاسبة فارغة.
مكدس الجهاز الظاهري Java: الخيوط خاصة ، ودورة حياتها متسقة مع المواضيع. عند تنفيذ كل طريقة ، سيتم إنشاء إطار مكدس لتخزين المعلومات مثل الجداول المتغيرة المحلية ، ومكدس المعامل ، والروابط الديناميكية ، وخارج الطريقة ، إلخ.
مكدس الطريقة المحلية: تشبه الوظائف مكدس الجهاز الظاهري ، باستثناء أن مكدس الجهاز الظاهري يؤدي خدمات طريقة Java للجهاز الظاهري ، في حين أن مكدس الطريقة المحلية يخدم الطريقة الأصلية المستخدمة.
Java Heap: إنها أكبر جزء من ذاكرة إدارة الجهاز الظاهري ، والمشاركة في جميع مؤشرات الترابط ، ويتم استخدام هذه المنطقة لتخزين مثيلات الكائنات ، ويتم تخصيص جميع الكائنات تقريبًا في هذا المجال. كومة Java هي المنطقة الرئيسية لإعادة تدوير الذاكرة. من منظور إعادة تدوير الذاكرة ، نظرًا لأن معظم جامعي الجمع الحاليين يستخدمون خوارزميات جمع الأجيال ، يمكن أيضًا تقسيم كومة Java إلى: الجيل الجديد والجيل القديم. إذا تم تقسيمها قليلاً ، فيمكن تقسيمها إلى مساحة عدن ، من مساحة الناجين ، إلى الفضاء الناجي ، وما إلى ذلك. وفقًا لمواصفات الجهاز الافتراضي Java ، يمكن أن يكون كومة Java في مساحة متقطعة جسديًا ، طالما أنها مستمرة منطقيًا.
منطقة الطريقة: مثل Java ، يتم مشاركتها بواسطة مؤشرات ترابط مختلفة وتستخدم لتخزين البيانات مثل معلومات الفئة التي تم تحميلها بواسطة الجهاز الظاهري ، دائمًا ، متغيرات ثابتة ، رمز تم تجميعه بواسطة برنامج التحويل البرمجي الفوري.
حمام السباحة الثابت في وقت التشغيل ، حمام السباحة الثابت في وقت التشغيل هو جزء من منطقة الطريقة. بالإضافة إلى إصدار الفصل ، والحقول والأساليب والواجهات ومعلومات الوصف الأخرى ، هناك أيضًا تجمع ثابت في ملف الفصل ، والذي يستخدم لتخزين مختلف المراجع الحرفية والرمزية التي تم إنشاؤها خلال فترة التجميع. أثناء وقت التشغيل ، يمكن وضع الثوابت الجديدة في البركة الثابتة. الأكثر استخدامًا هو طريقة المتدرب () لفئة السلسلة. عندما تستدعي مثيل سلسلة متدرب ، تجد Java ما إذا كانت هناك نفس ثوابت سلسلة Unicode في التجمع الثابت. إذا كان هناك ، فإنه يعيد مرجعه ؛ إذا لم يكن الأمر كذلك ، أضف Unicode مساوياً لسلسلة المثيل ويعيد مرجعه.
2. كيفية تحديد كائن القمامة
هناك العديد من مثيلات الكائن المخزنة في كومة جافا. قبل إعادة تدوير جامع القمامة الكومة ، يحتاج أولاً إلى تحديد الأشياء التي لا تزال "على قيد الحياة" والتي لها "ميتة" ، أي الكائنات التي لن يتم استخدامها بأي وسيلة.
اقتباس العد
طريقة حساب الاقتباس بسيطة في التنفيذ والكفاءة ، وهي خوارزمية جيدة في معظم الحالات. المبدأ هو: إضافة عداد مرجع إلى الكائن. كلما كان هناك مكان للرجوع إلى الكائن ، يتم زيادة العداد بمقدار 1. عند فشل المرجع ، يتم تقليل العداد بمقدار 1. عندما تكون قيمة العداد 0 ، فهذا يعني أن الكائن لم يعد يستخدم. تجدر الإشارة إلى أن طريقة العد المرجعية يصعب حل مشكلة المرجع المتبادل بين الكائنات ، وأن الأجهزة الافتراضية Java السائدة لا تستخدم طريقة حساب المرجع لإدارة الذاكرة.
خوارزمية تحليل إمكانية الوصول
تتمثل الفكرة الأساسية لهذه الخوارزمية في البحث لأسفل من خلال سلسلة من الكائنات تسمى "جذور GC" كنقطة انطلاق ، بدءًا من هذه العقد. يسمى المسار الذي تم تفتيشه من خلال سلسلة مرجعية. عندما لا يتم توصيل كائن بجذور GC دون أي سلسلة مرجعية (بكلمات نظرية الرسم البياني ، فإنه من جذور GC إلى هذا الكائن الذي لا يمكن الوصول إليه) ، فقد ثبت أن هذا الكائن غير متوفر. كما هو موضح في الشكل ، على الرغم من أن الكائنات 5 ، والكائن 6 ، والكائن 7 ، يرتبطون ببعضهما البعض ، إلا أنها لا يمكن الوصول إليها لجذور GC ، لذلك سيتم الحكم عليها على أنها كائنات قابلة لإعادة التدوير.
في لغة جافا ، تشمل الكائنات التالية التي يمكن استخدامها كجذور GC:
الكائن المشار إليه في مكدس الجهاز الظاهري (جدول المتغير المحلي في إطار المكدس).
الكائن المشار إليه بواسطة السمة الثابتة للفئة في منطقة الطريقة.
الكائن المشار إليه من قبل الثوابت في منطقة الطريقة.
الكائنات المشار إليها من قبل JNI (أي ، الطريقة الأصلية العامة) في مكدس الطريقة المحلية.
والسؤال الآن هو ، هل سيكون لخوارزمية تحليل إمكانية الوصول مشكلة مرجعية دائرية بين الكائنات؟ الجواب هو نعم ، أي لن تكون هناك مشكلة في الإشارة الدائرية بين الكائنات. جذر GC هو "نقطة انطلاق" محددة خصيصًا خارج الرسم البياني للكائن ولا يمكن الرجوع إليها بواسطة الكائنات في الرسم البياني للكائن.
للموت أو لا تموت
حتى الكائنات التي لا يمكن الوصول إليها في خوارزمية تحليل إمكانية الوصول ليست "يجب أن تموت". في هذا الوقت ، هم مؤقتًا في مرحلة "الاختبار". للإعلان حقًا عن كائن ميت ، يجب أن يمر بعملية علامات على الأقل: إذا وجد الكائن أنه لا توجد سلسلة مرجعية متصلة بجذور GC بعد إجراء تحليل إمكانية الوصول ، فسيتم تمييزها لأول مرة وتصفيتها. شرط التصفية هو ما إذا كان من الضروري لهذا الكائن تنفيذ طريقة Finapze (). عندما لا يقوم الكائن بالكتابة فوق طريقة finapze () ، أو تم استدعاء طريقة finapze () من قبل الجهاز الظاهري ، يعتبر الجهاز الظاهري كلتا الحالتين على أنهما "لا حاجة للتنفيذ". في البرنامج ، يمكنك الكتابة فوق Finapze () لإنشاء عملية تحديد ذاتي "مثيرة" ، ولكن هذه فرصة واحدة فقط.
/** * يوضح هذا الرمز نقطتين: * 1. يمكن للكائنات أن تنقذ نفسها عندما تكون GC. * 2. لا يوجد سوى فرصة واحدة للمرء الذاتي ، لأن طريقة finapze () لكائن ما لن يتم استدعاؤها تلقائيًا إلا مرة واحدة من قبل النظام على الأكثر * Author ZZM */ PUBPC class finapzeescapegc {pubpc static finapzeescapegc save_hook = null ؛ PubPC void isApve () {system.out.println ("نعم ، ما زلت apve :)") ؛ } override محمية void finapze () رمي {super.finapze () ؛ System.out.println ("Finapze Mehtod event!") ؛ finapzeescapegc.save_hook = هذا ؛ } pubpc static void main (string [] args) رميات {save_hook = new finapzeescapegc () ؛ // الكائن ينقذ نفسه بنجاح لأول مرة Save_Hook = null ؛ System.gc () ؛ // لأن طريقة finapze لها أولوية منخفضة ، وقفة لمدة 0.5 ثانية لانتظار thread.sleep (500) ؛ if (save_hook! = null) {save_hook.isapve () ؛ } آخر {system.out.println ("لا ، أنا ميت :(") ؛} // الكود التالي هو بالضبط ما سبق ، لكن هذه المرة فشل الرصيد الذاتي. save_hook = null ؛ system.gc () save_hook.isapve () ؛النتيجة الجارية هي:
نفذ Finapze Mehtod! نعم ، ما زلت apve :) لا ، أنا ميت :(
لنتحدث عن الاستشهادات
سواء كان ذلك يحكم على عدد مراجع كائن من خلال خوارزمية حساب مرجعية أو تحديد ما إذا كان يمكن الوصول إلى السلسلة المرجعية للكائن من خلال خوارزمية تحليل إمكانية الوصول ، وتحديد ما إذا كان بقاء الكائن مرتبطًا بـ "المرجع". قبل JDK 1.2 ، كان تعريف المراجع في Java تقليدية للغاية: إذا كانت القيمة المخزنة في بيانات النوع المرجعي تمثل عنوان بدء جزء آخر من الذاكرة ، يقال أن هذه القطعة من الذاكرة تمثل مرجعًا. بعد JDK 1.2 ، وسعت Java مفهوم المرجع ، وتقسيم المراجع إلى أربعة أنواع: مرجع قوي ، مرجع ناعم ، مرجع ضعيف ، ومرجع الوهمية. أضعف قوة هذه الأنواع الأربعة للمرجع تدريجيا بدورها.
• يشير الاقتباس القوي إلى المراجع الشائعة في رمز البرنامج ، مثل "Object OBJ = New Object ()". طالما أن الاقتباس القوي لا يزال موجودًا ، فلن يقوم جامع القمامة بإعادة تدوير الكائن المشار إليه.
• يتم استخدام المراجع اللينة لوصف بعض الكائنات المفيدة ولكن ليست ضرورية. بالنسبة للكائنات المرجعية المرجعية الناعمة ، سيتم سرد هذه الكائنات في نطاق إعادة التدوير لإعادة التدوير الثانية قبل أن يكون النظام على وشك الحصول على استثناء في التدفق. إذا لم يكن هناك ما يكفي من الذاكرة لإعادة التدوير هذه ، فسيتم إلقاء استثناء في التدفق الذاكرة. بعد JDK 1.2 ، يتم توفير فئة Softreference لتنفيذ المراجع الناعمة.
• يتم استخدام المراجع الضعيفة أيضًا لوصف الأشياء غير الضرورية ، لكن قوتها أضعف من المراجع الناعمة. يمكن للكائنات المرتبطة بالمراجع الضعيفة البقاء على قيد الحياة فقط حتى يحدث مجموعة القمامة التالية. عندما يعمل جامع القمامة ، يتم جمع الأشياء المرتبطة فقط بالمراجع الضعيفة بغض النظر عما إذا كانت الذاكرة الحالية كافية. بعد JDK 1.2 ، يتم توفير فئة الضعف لتنفيذ المراجع الضعيفة.
• يُطلق على عروض أسعار الفراغ أيضًا اقتباسات Ghost أو اقتباسات فانتوم ، وهي أضعف علاقة استشهاد. ما إذا كان الكائن له مرجع افتراضي لن يكون له أي تأثير على وقت البقاء على قيد الحياة على الإطلاق ، ولن يكون من الممكن الحصول على مثيل كائن من خلال المرجع الافتراضي. الغرض الوحيد من إعداد الجمعيات المرجعية الافتراضية لكائن ما هو تلقي إشعار النظام عند إعادة تدوير الكائن بواسطة المجمع. بعد JDK 1.2 ، يتم توفير فئة PhantomReference لتنفيذ المراجع الافتراضية.
مثال على الاستخدام المرجعي الناعم:
package jvm ؛ import java.lang.ref.softreference ؛ class node {pubpc string msg = "" ؛ // مرجع قوي node1.msg = "node1" ؛ softreference <Node> node2 = new softreference <Node> (node1) ؛ // Soft Reference Node2.get (). msg = "node2" ؛ system.out.println (node1.msg) ؛ system.out.println (node2.get (). msg) ؛}}نتيجة الإخراج هي:
node2node2
3. خوارزمية جمع القمامة النموذجية
1. خوارزمية SWEEP (MARK-CLEAR)
هذه هي خوارزمية مجموعة القمامة الأساسية. السبب في أنه يقال إنه الأكثر أساسية هو أنه أسهل تنفيذ وأبسط فكرة. تنقسم خوارزمية إزالة العلامات إلى مرحلتين: مرحلة العلامات ومرحلة المقاصة. تتمثل مهمة مرحلة العلامات في تحديد جميع الكائنات التي تحتاج إلى إعادة تدويرها ، ومرحلة المقاصة هي إعادة تدوير المساحة التي تشغلها الكائنات المحددة. يتم عرض العملية المحددة في الشكل أدناه:
يمكن أن نرى بسهولة من الشكل أن خوارزمية إزالة العلامات أسهل في التنفيذ ، ولكن هناك مشكلة خطيرة أنه من السهل إنشاء شظايا ذاكرة. قد يتسبب الكثير من الشظايا في عدم القدرة على إيجاد مساحة كافية عند تخصيص مساحة للكائنات الكبيرة في العملية اللاحقة ، وتشغيل إجراء جديد لجمع القمامة مقدمًا.
2. نسخ خوارزمية
من أجل حل أوجه القصور في خوارزمية مارك-Sweep ، تم اقتراح خوارزمية النسخ. إنه يقسم الذاكرة المتاحة إلى قطعتين من الحجم المتساوي حسب السعة ، باستخدام قطعة واحدة فقط في وقت واحد. عند استخدام قطعة الذاكرة هذه لأعلى ، انسخ الكائن الذي لا يزال قائماً إلى قطعة أخرى ، ثم قم بتنظيف مساحة الذاكرة المستخدمة في وقت واحد ، حتى لا تحدث مشاكل تجزئة الذاكرة. يتم عرض العملية المحددة في الشكل أدناه:
على الرغم من أن هذه الخوارزمية سهلة التنفيذ ، فهي فعالة في التشغيل وليس من السهل إنشاء تجزئة الذاكرة ، إلا أنها مكلفة استخدام مساحة الذاكرة لأن الذاكرة التي يمكن استخدامها تقل إلى نصف الأصلي.
من الواضح أن كفاءة خوارزمية النسخ لها علاقة كبيرة بعدد الكائنات الباقية. إذا كان هناك العديد من الأشياء الباقية ، فسيتم تقليل كفاءة خوارزمية النسخ بشكل كبير.
3. خوارزمية مارك-كومباكت (علامة مارك)
من أجل حل أوجه القصور في خوارزمية النسخ واستفادة كاملة من مساحة الذاكرة ، يتم اقتراح خوارزمية Mark-Compact. تمثل الخوارزمية نفس العلامة ، ولكن بعد الانتهاء من العلامة ، لا تنظف مباشرة الكائنات القابلة لإعادة التدوير ، ولكنها تنقل جميع الكائنات الحية إلى نهاية واحدة ثم تنظف الذاكرة خارج حدود النهاية. يتم عرض العملية المحددة في الشكل أدناه:
4. خوارزمية جمع الأجيال
تستخدم خوارزمية مجموعة الجيل حاليًا من قبل معظم جامعي القمامة JVM. فكرتها الأساسية هي تقسيم الذاكرة إلى عدة مناطق مختلفة وفقًا لدورة حياة بقاء الكائن. بشكل عام ، تنقسم منطقة الكومة إلى الجيل القديم والجيل الشاب. تتمثل خاصية الجيل القديم في أن هناك عددًا صغيرًا فقط من الأشياء التي يجب إعادة تدويرها في كل مرة يتم فيها جمع القمامة ، في حين أن خاصية الجيل الجديد هي أن هناك عددًا كبيرًا من الكائنات يجب إعادة تدويرها في كل مرة يتم فيها جمع القمامة. ثم يمكن اعتماد خوارزمية التجميع الأنسب وفقًا لخصائص الأجيال المختلفة.
في الوقت الحالي ، يتبنى معظم جامعي القمامة خوارزمية النسخ للجيل الجديد ، لأنه في الجيل الجديد ، يجب إعادة تدوير معظم الأشياء في كل مرة ، أي أن عدد العمليات التي تحتاج إلى نسخها صغيرة ، ولكن في الواقع ، لا يتم تقسيم مساحة الجيل الجديد وفقًا لنسبة 1: 1. بشكل عام ، ينقسم الجيل الجديد إلى مساحة أكبر عدن ومساحة أصغر من الناجين (عادة 8: 1: 1). في كل مرة يتم فيها استخدام مساحة عدن وأحد مساحات الناجين ، عند إعادة التدوير ، يتم تنظيف الكائنات التي لا تزال تعيش في عدن والناجين إلى مساحة أخرى من الناجين ، ثم يتم تنظيف عدن والمساحات الناجية التي تم استخدامها للتو.
نظرًا لأن الشيخوخة هي أنه يتم إعادة تدوير عدد صغير فقط من الكائنات في كل مرة ، يتم استخدام خوارزمية Mark-Compact بشكل عام.
التحليل الموجز أعلاه لنموذج ذاكرة Java وجمع القمامة هو كل المحتوى الذي أشاركه معك. آمل أن تتمكن من إعطائك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.