يعد نموذج ذاكرة Java ، الذي يشار إليه باسم JMM ، ضمانًا موحدًا لسلسلة من منصات الجهاز الظاهري Java إلى النظام الأساسي المحدد غير المرتبط برؤية الذاكرة وما إذا كان يمكن إعادة ترتيبها في بيئة متعددة الخيوط يقدمها المطورين. (قد يكون هناك غامض من حيث المصطلح وتوزيع الذاكرة لوقت تشغيل Java ، والذي يشير إلى مناطق الذاكرة مثل الكومة ، ومنطقة الطريقة ، ومكدس الخيط ، إلخ).
هناك العديد من أنماط البرمجة المتزامنة. بالإضافة إلى CSP (عملية متسلسلة الاتصالات) ، والممثل والموديلات الأخرى ، ينبغي أن يكون النموذج الأكثر دراية هو نموذج الذاكرة المشتركة استنادًا إلى مؤشرات الترابط والأقفال. في البرمجة متعددة الخيوط ، يجب الاهتمام بثلاثة أنواع من مشاكل التزامن:
・ Atomicity ・ الرؤية ・ إعادة ترتيب
يتضمن Atomicity ما إذا كان بإمكان مؤشرات الترابط الأخرى رؤية الحالة الوسيطة أو تتداخل عندما يقوم مؤشر ترابط بتشغيل مركب. عادة ، هذه هي مشكلة i ++. يقوم ترابطان بإجراء عمليات ++ على ذاكرة الكومة المشتركة في نفس الوقت. قد يكون تنفيذ عمليات ++ في JVM ووقت التشغيل ووحدة المعالجة المركزية عملية مركبة. على سبيل المثال ، من منظور تعليمات JVM ، فإن قراءة قيمة I من ذاكرة الكومة إلى مكدس المعامل ، وإضافة واحدة ، والكتابة إلى ذاكرة الكومة i. خلال هذه العمليات ، إذا لم يكن هناك مزامنة صحيحة ، فيمكن أن تنفذها مؤشرات الترابط الأخرى في نفس الوقت ، مما قد يؤدي إلى فقدان البيانات ومشاكل أخرى. يتم الحكم على مشاكل الذرة المشتركة ، والمعروفة أيضًا باسم الحالة التنافسية ، بناءً على نتيجة فشل محتملة ، مثل القراءة والكتابة. الرؤية وإعادة ترتيب المشكلات تنبع من تحسين النظام.
نظرًا لأن سرعة تنفيذ وحدة المعالجة المركزية وسرعة الوصول للذاكرة غير متطابقة بشكل خطير ، من أجل تحسين الأداء ، استنادًا إلى مبادئ التوطين مثل موقع الوقت والمكان المكاني ، أضافت وحدة المعالجة المركزية ذاكرة التخزين المؤقت متعددة الطبقات بين الذاكرة. عندما يكون من الضروري جلب البيانات ، ستذهب وحدة المعالجة المركزية أولاً إلى ذاكرة التخزين المؤقت لمعرفة ما إذا كانت ذاكرة التخزين المؤقت المقابلة موجودة. إذا كان موجودًا ، فسيتم إرجاعه مباشرة. إذا لم يكن موجودًا ، فسيتم جلبه في الذاكرة ويتم حفظه في ذاكرة التخزين المؤقت. الآن أصبحت المعالجات المتعددة النوى أكثر قياسية ، كل معالج لديه ذاكرة التخزين المؤقت الخاصة به ، والتي تتضمن مشكلة تناسق ذاكرة التخزين المؤقت. وحدات المعالجة المركزية لها نماذج الاتساق من نقاط القوة والضعف المختلفة. أقوى الاتساق هو أعلى أمان ، كما أنه يتوافق مع وضع تفكيرنا المتسلسل. ومع ذلك ، من حيث الأداء ، سيكون هناك الكثير من النفقات العامة بسبب الحاجة إلى التواصل المنسق بين مختلف وحدات المعالجة المركزية.
مخطط هيكل ذاكرة التخزين المؤقت للوحدة المعالجة المركزية هو كما يلي
عادةً ما تكون دورة التعليمات الخاصة بوحدة المعالجة المركزية هي تعليمات جلب ، وتحليل التعليمات لقراءة البيانات ، وتنفيذ التعليمات ، وكتابة البيانات مرة أخرى إلى السجلات أو الذاكرة. عند تنفيذ التعليمات في المسلسل ، تستغرق البيانات القراءة والتخزين وقتًا طويلاً ، وبالتالي فإن وحدة المعالجة المركزية تستخدم عمومًا خط أنابيب التعليمات لتنفيذ تعليمات متعددة في نفس الوقت لتحسين الإنتاجية الكلية ، تمامًا مثل خط أنابيب المصنع.
إن سرعة قراءة البيانات وكتابة البيانات مرة أخرى إلى الذاكرة ليست على نفس الترتيب من حيث الحجم من تنفيذ التعليمات ، وبالتالي فإن وحدة المعالجة المركزية تستخدم السجلات وذاكرة التخزين المؤقت مثل المخازن المؤقتة. عند قراءة البيانات من الذاكرة ، ستقرأ خط ذاكرة التخزين المؤقت (على غرار قراءة القرص وقراءة كتلة). ستضع الوحدة النمطية التي تكتب مرة أخرى طلب التخزين في المخزن المؤقت للمخزن عندما لا تكون البيانات القديمة في ذاكرة التخزين المؤقت وتستمر في تنفيذ المرحلة التالية من دورة التعليمات. إذا كانت موجودة في ذاكرة التخزين المؤقت ، فسيتم تحديث ذاكرة التخزين المؤقت ، وسيتم تدفق البيانات الموجودة في ذاكرة التخزين المؤقت إلى الذاكرة وفقًا لسياسة معينة.
الطبقة العامة MemoryModel {private int count ؛ توقف منطقي خاص ؛ public void initCountandStop () {count = 1 ؛ توقف = خطأ ؛ } public void doloop () {بينما (! stop) {count ++ ؛ }} public void printresult () {system.out.println (count) ؛ system.out.println (stop) ؛ }}عند تنفيذ الكود أعلاه ، قد نعتقد أنه سيتم تنفيذ العد = 1 قبل التوقف = خطأ. هذا صحيح في الحالة المثالية الموضحة في مخطط تنفيذ وحدة المعالجة المركزية أعلاه ، لكنه غير صحيح عند النظر في التسجيل والتخزين المؤقت لذاكرة التخزين المؤقت. على سبيل المثال ، توقف عن نفسه في ذاكرة التخزين المؤقت ولكن العدد ليس موجودًا ، ثم قد يتم تحديث التوقف ويتم تحديث المخزن المؤقت للكتابة للكتابة إلى الذاكرة قبل الكتابة مرة أخرى.
بالإضافة إلى ذلك ، يجوز لوحدة المعالجة المركزية والمترجم (عادةً ما تشير إلى JIT لـ JAVA) تعديل ترتيب تنفيذ التعليمات. على سبيل المثال ، في الكود أعلاه ، العد = 1 وإيقاف = خطأ ليس لديهم تبعيات ، لذلك يجوز لوحدة المعالجة المركزية والمترجم تعديل ترتيب هذين. في عرض برنامج واحد متخلف ، والنتيجة هي نفسها. هذا هو أيضًا ما إذا كان يجب على وحدة المعالجة المركزية والمترجم ضمان (بغض النظر عن كيفية تعديل أمر التنفيذ ، تظل نتيجة التنفيذ للخيوط الواحدة دون تغيير). نظرًا لأن معظم تنفيذ البرنامج متمرس ، فإن هذا التحسين مقبول ويحقق تحسينات رائعة في الأداء. ومع ذلك ، في حالة MultiThreading ، قد تحدث نتائج غير متوقعة بدون عمليات التزامن اللازمة. على سبيل المثال ، بعد تنفيذ THREED T1 طريقة initCOUNTANDSTOP ، يقوم مؤشر الترابط T2 بتنفيذ printresult ، والذي قد يكون 0 ، خطأ ، 1 ، خطأ ، أو 0 ، صحيح. إذا قام مؤشر الترابط T1 بتنفيذ Doloop () أولاً وتنفيذ مؤشر الترابط T2 EnitCountandStop ثانية واحدة ، فقد يقفز T1 من الحلقة ، أو قد لا يرى تعديل التوقف بسبب تحسين المترجم.
نظرًا للمشاكل المختلفة في المواقف المتعددة المذكورة أعلاه ، لم يعد تسلسل البرنامج في متعدد الخيوط أمر التنفيذ ويؤدي إلى الآلية الأساسية. تحتاج لغة البرمجة إلى منح المطورين ضمانًا. بعبارات بسيطة ، يكون هذا الضمان هو عندما يكون تعديل مؤشر الترابط مرئيًا للمواضيع الأخرى. لذلك ، تقترح لغة Java JavamemoryModel ، أي نموذج ذاكرة Java ، والذي يتطلب التنفيذ وفقًا لاتفاقيات هذا النموذج. توفر Java آليات مثل المتقلبة والمزامنة والنهائية لمساعدة المطورين على ضمان صحة البرامج متعددة الخيوط على جميع منصات المعالجات.
قبل JDK1.5 ، واجه نموذج ذاكرة Java مشاكل خطيرة. على سبيل المثال ، في نموذج الذاكرة القديم ، قد يرى مؤشر ترابط القيمة الافتراضية للحقل النهائي بعد اكتمال المُنشئ ، ويمكن إعادة ترتيب كتابة الحقل المتطاير مع قراءة وكتابة الحقل غير المتطابق.
لذلك في JDK1.5 ، تم اقتراح نموذج ذاكرة جديد من خلال JSR133 لإصلاح المشكلات السابقة.
إعادة ترتيب القواعد
قفل متطاير ومراقبة
| هل من الممكن إعادة ترتيب | العملية الثانية | العملية الثانية | العملية الثانية |
|---|---|---|---|
| العملية الأولى | القراءة العادية/الكتابة العادية | تقلص القراءة/المراقبة أدخل | متقلبة الكتابة/مراقبة الخروج |
| القراءة العادية/الكتابة العادية | لا | ||
| voaltile قراءة/مراقبة إدخال | لا | لا | لا |
| متقلبة الكتابة/مراقبة الخروج | لا | لا |
تشير القراءة العادية إلى صفيف صفيف GetField و GetStatic وغير المتقلبة ، وتشير القراءة العادية إلى Arraystore of Putfield و Putstatic وغير الممتاز.
قراءة وكتابة الحقول المتقلبة هي Getfield ، GetStatic ، Putfield ، putstatic ، على التوالي.
تتمثل المراقبة في إدخال كتلة التزامن أو طريقة التزامن ، ويشير Monitorexist إلى الخروج من كتلة التزامن أو طريقة التزامن.
لا في الجدول أعلاه يشير إلى عمليتين لا تسمح بإعادة ترتيب. على سبيل المثال (الكتابة العادية ، الكتابة المتطايرة) تشير إلى إعادة ترتيب الحقول غير المتطايرة وإعادة ترتيب كتابة أي حقول متقلبة لاحقة. عندما لا يكون هناك لا ، فهذا يعني أن إعادة الترتيب مسموح بها ، ولكن يحتاج JVM إلى ضمان الحد الأدنى من الأمان - القيمة هي إما القيمة الافتراضية أو المكتوبة من قبل مؤشرات الترابط الأخرى (64 بت من القراءة والكتابة الطويلة هي حالة خاصة. عندما لا يكون هناك تعديل متقلبة ، لا يضمن القراءة والكتابة الذرية ، وطبقة الطبقة الأساسية قد تنقسم إلى اثنين من العمليات المنفصلة).
الحقل النهائي
هناك قواعد خاصة إضافية للحقل النهائي
لا يمكن إعادة ترتيب كتابة الحقل النهائي (في المُنشئ) أو كتابة مرجع كائن الحقل النهائي نفسه مع عمليات كتابة لاحقة للكائنات التي تحمل الحقل النهائي (خارج المنشئ). على سبيل المثال ، لا يمكن إعادة ترتيب البيان التالي
x.finalfield = v ؛ ... shareRef = x ؛
لا يمكن إعادة ترتيب الحمل الأول من الحقل النهائي مع كتابة الكائن الذي يحمل الحقل النهائي. على سبيل المثال ، لا يسمح البيان التالي بإعادة ترتيب.
x = sharedRef ؛ ... i = x.finalfield
حاجز الذاكرة
تدعم المعالجات جميعها حواجز أو أسوار للذاكرة للتحكم في رؤية إعادة الترتيب والبيانات بين المعالجات المختلفة. على سبيل المثال ، عندما تكتب وحدة المعالجة المركزية البيانات مرة أخرى ، ستضع طلب المتجر في المخزن المؤقت للكتابة وينتظر التدفق في الذاكرة. يمكن منع طلب المتجر هذا من إعادة ترتيبها من خلال طلبات أخرى عن طريق إدخال الحاجز لضمان رؤية البيانات. يمكنك استخدام مثال الحياة لمقارنة الحاجز. على سبيل المثال ، عند أخذ مصعد منحدر في مترو الأنفاق ، يدخل الجميع المصعد بالتسلسل ، لكن بعض الأشخاص سوف يتجولون من اليسار ، وبالتالي فإن الترتيب عند ترك المصعد مختلف. إذا كان الشخص يحمل أمتعة كبيرة محجوبة (حاجز) ، فلن يتمكن الأشخاص الذين يقفون وراءهم :). بالإضافة إلى ذلك ، فإن الحاجز هنا وحاجز الكتابة المستخدم في GC هما مفاهيم مختلفة.
تصنيف حواجز الذاكرة
تدعم جميع المعالجات تقريبًا تعليمات الحاجز لحبوب معينة من الخشنة ، والتي تسمى عادة السياج (السياج ، السياج) ، والتي يمكن أن تضمن أن تعليمات الحمل والتخزين التي بدأت قبل السياج يمكن أن تكون بالترتيب الصارم مع الحمل والتخزين بعد السياج. عادة ، سيتم تقسيمه إلى الأنواع الأربعة التالية من الحواجز وفقًا لغرضها.
حواجز تحميل
load1 ؛ تحميل load2 ؛
تأكد من تحميل بيانات LOAD1 قبل LOAD2 وبعد التحميل
حواجز Storestore
store1 ؛ Storestore Store2
تأكد من أن البيانات في Store1 مرئية للمعالجات الأخرى قبل Store2 وبعد.
حواجز LoadStore
load1 ؛ LoadStore Store2
تأكد من تحميل بيانات LOAD1 قبل STORE2 وبعد تدفق البيانات
حواجز storeload
store1 ؛ storeload load2
تأكد من أن البيانات في Store1 مرئية أمام المعالجات الأخرى (مثل التدفق إلى الذاكرة) قبل تحميل البيانات في load2 وبعد التحميل. يمنع حاجز Storeload الحمل من قراءة البيانات القديمة بدلاً من البيانات التي كتبها المعالجات الأخرى مؤخرًا.
تتطلب جميع المعالجات المتعددة تقريبًا في العصر الحديث storeload. عادةً ما تكون النفقات العامة من Storeload هي الأكبر ، ويكون لـ Storeload تأثير ثلاثة حواجز أخرى ، لذلك يمكن استخدام Storeload كحاجز عام (ولكن أعلى).
لذلك ، باستخدام حاجز الذاكرة أعلاه ، يمكن تنفيذ قواعد إعادة ترتيب في الجدول أعلاه
| بحاجة إلى حواجز | العملية الثانية | العملية الثانية | العملية الثانية | العملية الثانية |
|---|---|---|---|---|
| العملية الأولى | القراءة العادية | الكتابة العادية | تقلص القراءة/المراقبة أدخل | متقلبة الكتابة/مراقبة الخروج |
| القراءة العادية | LoadStore | |||
| القراءة العادية | Storestore | |||
| voaltile قراءة/مراقبة إدخال | تحميل | LoadStore | تحميل | LoadStore |
| متقلبة الكتابة/مراقبة الخروج | storeload | Storestore |
من أجل دعم قواعد الحقول النهائية ، من الضروري إضافة حاجز على الكتابة النهائية إلى النهائي
x.finalfield = v ؛ Storestore shareRef = x ؛
أدخل حاجز الذاكرة
استنادًا إلى القواعد المذكورة أعلاه ، يمكنك إضافة حاجز إلى معالجة الحقول المتطايرة والكلمات الرئيسية المتزامنة لتلبية قواعد نموذج الذاكرة.
أدخل Storestore قبل حاجز المتجر المتطرف بعد كتابة جميع الحقول النهائية ولكن أدخل Storestore قبل عودة المنشئ
أدخل حاجز Storeload بعد المتجر المتقلبة. أدخل حاجز Loadload و LoadStore بعد الحمل المتطاير.
تتسق قواعد Enter و Result Folatile Roach ، وقواعد خروج الشاشة وقواعد المتجر المتقلبة.
يحدث قبل
لا تزال الحواجز المختلفة للذاكرة المذكورة أعلاه معقدة نسبيًا للمطورين ، بحيث يمكن لـ JMM استخدام سلسلة من قواعد علاقات النظام الجزئي من خلال التوضيح. للتأكد من أن الخيط الذي ينفذ العملية B يرى نتيجة العملية A (بغض النظر عما إذا كان يتم تنفيذ A و B في نفس الخيط) ، يجب أن يتم استيفاء العلاقة بين A و B ، وإلا يمكن لـ JVM إعادة ترتيبهما بشكل تعسفي.
تحدث قائمة القواعد
تتضمن قواعد Happendbefore
قواعد تسلسل البرنامج: إذا كانت العملية A في البرنامج قبل العملية B ، فإن التشغيل A في نفس الخيط ستؤدي قواعد قفل الشاشة قبل العملية B: يجب إجراء عملية القفل على قفل الشاشة قبل عملية القفل على قفل الشاشة نفسه.
قواعد متغيرة متطايرة: يجب أن تنفذ عملية الكتابة للمتغير المتطرف قواعد بدء تشغيل مؤشرات الترابط قبل تشغيل القراءة للمتغير: الدعوة إلى الموضوع. يتم تنفيذ العملية B قبل العملية C ، ثم يتم تنفيذ العملية A قبل العملية C.
يحتوي قفل العرض على نفس دلالات الذاكرة مثل قفل الشاشة ، والمتغير الذري له نفس دلالات الذاكرة مثل المتقلبة. إن عملية الاستحواذ على الأقفال وإصدارها ، وعمليات قراءة وكتابة المتغيرات المتطايرة تفي بالعلاقة الكاملة ، بحيث يمكن إجراء كتابة متقلبة قبل القراءات المتطايرة اللاحقة.
يمكن الجمع بين الحدوث المذكورة أعلاه باستخدام قواعد متعددة.
على سبيل المثال ، بعد مؤشر الترابط A ، تدخل قفل الشاشة ، وتستند العملية قبل إطلاق قفل الشاشة إلى قواعد تسلسل البرنامج ، ويتم استخدام عملية إصدار الشاشة للحصول على نفس قفل الشاشة في الخيط التالي ب ، والتشغيل في العملية في حدث PRODER B.
لخص
ما سبق هو كل التفسير التفصيلي لنموذج ذاكرة Java JMM في هذه المقالة ، وآمل أن يكون مفيدًا للجميع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع!