[C++] RAII مقابل جامع القمامة



Answers

جمع القمامة يحل فئات معينة من مشاكل الموارد التي لا يمكن حل RAII. أساسا ، يتلخص في الاعتماد على التبعيات الدائرية حيث لا تحدد الدورة قبل اليد.

هذا يعطيها مزايا اثنين. أولاً ، ستكون هناك أنواع معينة من المشكلات لا يمكن لـ RAII حلها. هذه ، في تجربتي ، نادرة.

أكبر واحد هو أنه يسمح للمبرمج أن يكون كسول ولا يهتمون بأعمار موارد الذاكرة وبعض الموارد الأخرى التي لا تمانع في تأخير التنظيف. عندما لا يكون عليك الاهتمام ببعض أنواع المشاكل ، يمكنك الاهتمام بالمشاكل الأخرى. يتيح لك هذا التركيز على أجزاء المشكلة التي تريد التركيز عليها.

الجانب السلبي هو أنه بدون RAII ، فإن إدارة الموارد التي ترغب في تقييدها مدى الحياة أمر صعب. تخفّض لغات GC بشكل أساسي إما أن يكون لديك عمر افتراضي بسيط للغاية أو تتطلب منك إدارة الموارد يدويًا ، كما هو الحال في C ، مع ذكر يدويًا أنك انتهيت من مورد. يرتبط نظام عمر الكائن الخاص به ارتباطًا وثيقًا بجهاز GC ، ولا يعمل بشكل جيد مع إدارة العمر المعقد لأنظمة معقدة كبيرة (ومع ذلك لا تتطلب دورة).

لكي نكون منصفين ، فإن إدارة الموارد في C ++ تتطلب الكثير من العمل للقيام بشكل صحيح في مثل هذه الأنظمة الكبيرة المعقدة (التي لا تتطلب دورة). إن لغة C # واللغات المماثلة تجعلها أكثر صعوبة ، وفي المقابل فإنها تجعل الحالة السهلة سهلة.

تقوم معظم تطبيقات GC بفرض الفئات الكاملة غير المحلية. إنشاء المخازن المؤقتة المتجاورة للكائنات العامة ، أو إنشاء كائنات عامة في كائن واحد أكبر ، ليس شيئًا يسهل تنفيذ معظم عمليات GC. من ناحية أخرى ، يسمح لك C # بإنشاء struct نوع قيمة ذات إمكانات محدودة إلى حد ما. في العصر الحالي لمعمارية وحدة المعالجة المركزية ، يعتبر التوافق مع ذاكرة التخزين المؤقت أمرًا رئيسيًا ، كما أن عدم وجود قوات GC المحلية هو عبء ثقيل. بما أن هذه اللغات لها وقت تشغيل bytecode للجزء الأكبر ، من الناحية النظرية ، يمكن لبيئة JIT نقل البيانات المستخدمة بشكل شائع معًا ، ولكن في أغلب الأحيان تحصل فقط على فقدان أداء موحد بسبب وجود أخطاء في ذاكرة التخزين المؤقت المتكررة مقارنة بـ C ++.

تتمثل المشكلة الأخيرة مع GC في أن التخصيص غير محدد ، ويمكن أن يتسبب في بعض الأحيان في حدوث مشكلات في الأداء. تجعل GCs الحديثة هذه المشكلة أقل مما كانت عليه في الماضي.

Question

شاهدت مؤخراً حديثًا رائعًا من قبل Herb Sutter حول "Leak Free C ++ ..." في CppCon 2016 حيث تحدث عن استخدام مؤشرات ذكية لتنفيذ RAII (تتم عملية استحواذ الموارد) - المفاهيم وكيفية حل معظم مشكلات تسرب الذاكرة.

الآن كنت أتساءل. إذا اتبعت قواعد RAII بشكل صارم ، والتي يبدو أنها أمر جيد ، فلماذا يكون ذلك مختلفًا عن وجود جامع بيانات قمامة في C ++؟ وأنا أعلم أنه مع RAII للمبرمج هو في السيطرة الكاملة على متى يتم تحرير الموارد مرة أخرى ، ولكن هل هذا في أي حال من المفيد فقط وجود جامع القمامة؟ هل سيكون حقا أقل كفاءة؟ حتى أنني سمعت أن وجود جامع قمامة يمكن أن يكون أكثر كفاءة ، لأنه يمكن تحرير أجزاء كبيرة من الذاكرة في كل مرة بدلاً من تحرير أجزاء صغيرة من الذاكرة في جميع أنحاء التعليمات البرمجية.




تحدث تقريبا. قد يكون المصطلح RAII أفضل لوقت الاستجابة والارتعاش . قد يكون مجمّع garbage أفضل من أجل سرعة نقل النظام.




يحل RAII و GC المشاكل في اتجاهات مختلفة تمامًا. إنها مختلفة تمامًا ، على الرغم مما قد يقوله البعض.

كلاهما يعالج المشكلة بأن إدارة الموارد صعبة. مجموعة القمامة يحلها بجعلها حتى لا يحتاج المطور إلى دفع الكثير من الاهتمام لإدارة هذه الموارد. يحل RAII ذلك بجعله من السهل على مطوري البرامج الانتباه إلى إدارة مواردهم. أي شخص يقول أنه يفعل الشيء نفسه لديه شيء ليبيعك.

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

  • تقدم C ++ RAII من خلال المنشئ / destructors و GC من خلال shared_ptr (إذا جاز لي أن أجعل الحجة أن refcounting و GC في نفس الفئة من الحلول لأن كلاهما مصمم لمساعدتك ليس بحاجة إلى الانتباه إلى العمر الافتراضي)
  • تقدم بايثون RAII من خلال و GC من خلال نظام refack بالإضافة إلى جامع القمامة
  • تقدم C # RAII من خلال IDisposable using و GC من خلال جامع القمامة للأجيال

الأنماط هي في كل لغة.




يدعم كل من جمع البيانات المهملة و RAII كل بناء مشترك لا يكون الآخر مناسبًا له.

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

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

الاختلاف الرئيسي بين الحالات التي يفوز فيها GC مقابل تلك التي يفوز بها RAII هو أن GC جيد في إدارة الذاكرة المستبدلة التي يمكن تحريرها على أساس ما هو مطلوب ، ولكنها ضعيفة عند التعامل مع الموارد غير القابلة للاستبدال. RAII جيد في التعامل مع الأشياء ذات الملكية الواضحة ، ولكنها سيئة في التعامل مع حاملي البيانات غير المالكين الذين لا يملكون أي هوية حقيقية بصرف النظر عن البيانات التي تحتوي عليها.

نظرًا لعدم معالجة GC أو RAII لجميع السيناريوهات بشكل جيد ، فسيكون من المفيد للغات تقديم دعم جيد لكل منهما. لسوء الحظ ، فإن اللغات التي تركز على واحد تميل إلى التعامل مع الآخر على أنه فكرة لاحقة.




حتى أنني سمعت أن وجود جامع قمامة يمكن أن يكون أكثر كفاءة ، لأنه يمكن تحرير أجزاء كبيرة من الذاكرة في كل مرة بدلاً من تحرير أجزاء صغيرة من الذاكرة في جميع أنحاء التعليمات البرمجية.

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

وبالطبع ، فإنك ستدخل في مسألة متى يجب إلغاء تخصيص كل شيء - سواء كان استخدام تلك المخصّصات (أو لوح الذاكرة المقترن به) يجب أن يكون RAIIed أم لا ، وكيف.




"فعال" هو مصطلح واسع جدا ، بمعنى جهود التنمية RAII عادة ما تكون أقل كفاءة من GC ، ولكن من حيث الأداء GC عادة ما تكون أقل كفاءة من RAII. ومع ذلك ، فمن الممكن تقديم أمثلة contr لكلتا الحالتين. قد يكون التعامل مع GC العام عندما يكون لديك موارد واضحة للغاية (دي) لتخصيص اللغات في اللغات المدارة أمرًا مزعجًا إلى حد ما ، تمامًا مثل الكود الذي يستخدم RAII يمكن أن يكون غير فعال بشكل مفاجئ عند استخدام shared_ptr لكل شيء بدون سبب.




Links