كيفية التعامل مع RealLoc عندما تفشل بسبب الذاكرة؟

StackOverflow https://stackoverflow.com/questions/1986538

  •  22-09-2019
  •  | 
  •  

سؤال

يقول السؤال كله ولكن هنا مثال:

typedef struct mutable_t{
    int count, max;
    void **data;
} mutable_t;


void pushMutable(mutable_t *m, void *object)
{
    if(m->count == m->max){
        m->max *= 2;
        m->data = realloc(m->data, m->max * sizeof(void*));
    }
    // how to handle oom??
    m->data[m->count++] = object;
}

كيف يمكنني التعامل مع نفاد الذاكرة وليس إلغاء كل بياناتي؟

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

هل كانت مفيدة؟

المحلول

تتمثل التقنية القياسية في تقديم متغير جديد لعقد العائد من RealLoc. يمكنك بعد ذلك الكتابة فوق متغير الإدخال الخاص بك إذا نجحت:

tmp = realloc(orig, newsize);
if (tmp == NULL)
{
    // could not realloc, but orig still valid
}
else
{
    orig = tmp;
}

نصائح أخرى

الاستراتيجية حول ما يجب القيام به عندما realloc() الفشل يعتمد على طلبك. السؤال عام لا يمكن الإجابة عليه لجميع الحالات الممكنة.

بعض الملاحظات الأخرى:

لاتفعل ابدا:

a = realloc(a, size);

لو realloc() تفشل ، تفقد المؤشر الأصلي ، و realloc() لا free() الذاكرة الأصلية ، لذلك سوف تحصل على تسرب الذاكرة. بدلا من ذلك ، افعل:

tmp = realloc(a, size);
if (tmp)
    a = tmp;
else
    /* handle error */

النقطة الثانية التي أريد أن أصنعها هي بسيطة وقد لا تكون بهذه الأهمية ، ولكن من الجيد معرفة ذلك على أي حال: زيادة الذاكرة التي يتم تخصيصها بعامل f جيد. دعنا نقول لك malloc() ن بايت أولا. ثم تحتاج إلى المزيد من الذاكرة ، لذلك أنت realloc() مع الحجم n × f. ثم تحتاج إلى المزيد من الذاكرة ، لذلك تحتاج n × f2 بايت. إن أردت realloc() لاستخدام المساحة من كتل الذاكرة السابقة ، تريد التأكد من ذلك n × f2 ≤ n + n × f. حل هذه المعادلة ، نحصل عليها F≤ (SQRT (5) +1)/2 = 1.618 (ال النسبة الذهبية). أنا استخدم عامل 1.5 معظم الأوقات.

هذا هو موضوع زر ساخن لأن هناك مدرستين للتفكير في هذا الموضوع بشكل أساسي

  1. اكتشف OOM ، وإرجاع الوظيفة إلى رمز الخطأ.
  2. اكتشف OOM وتعطل عمليتك في أسرع وقت ممكن

أنا شخصياً في المخيم رقم 2. توقع لأنواع خاصة جدًا من التطبيقات ، OOM هي فترة قاتلة. صحيح ، يمكن للكود المكتوب تمامًا التعامل مع OOM ولكن قلة قليلة من الناس يفهمون كيفية كتابة التعليمات البرمجية الآمنة في مواجهة عدم وجود ذاكرة. حتى أقل من ذلك يزعجك فعل ذلك بالفعل لأنه لا يستحق كل هذا الجهد تقريبًا.

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

القاعدة الأولى التي تتابعها عند العمل مع realloc ليس لتعيين قيمة الإرجاع realloc إلى نفس المؤشر الذي انتقلت إليه. هذه

m->data = realloc(m->data, m->max * sizeof(void*)); 

سيء. لو realloc يفشل ، فإنه يعيد المؤشر الفارغ ، لكنه لا يعامل الذاكرة القديمة. الرمز أعلاه سوف لاغية m->data بينما تشير كتلة الذاكرة القديمة سابقًا m->data من المرجح أن يصبح تسرب الذاكرة (إذا لم يكن لديك أي إشارات أخرى).

قيمة الإرجاع realloc يجب تخزينها في مؤشر منفصل أولاً

void **new_data;
...
new_data = realloc(m->data, m->max * sizeof(void*)); 

ثم يمكنك التحقق من النجاح/الفشل وتغيير قيمة m->data في حالة النجاح

if (new_data != NULL)
  m->data = new_data;
else
  /* whatever */;

هذه مشكلتك تمامًا! فيما يلي بعض المعايير:

  • لقد طلبت تلك الذاكرة لسبب ما. إذا لم يكن متاحًا ، فهل عمل برنامجك محكوم عليه أم أنه يمكن أن يستمر في القيام بالأشياء؟ إذا كنت تريد إنهاء البرنامج برسالة خطأ ؛ خلاف ذلك ، يمكنك عرض رسالة خطأ بطريقة أو بأخرى والاستمرار.

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

  • هل سيكون من الخطأ أن يستمر برنامجك في التعرج بدون هذه البيانات وليس ما يكفي من الذاكرة؟ إذا كان الأمر كذلك ، فيجب أن تنتهي برسالة خطأ. من الأفضل قتل برنامجك بدلاً من مواصلة معالجة البيانات غير الصحيحة بشكل أعمى.

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

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

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

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

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

اختبار الحمض بالطبع هو أنك ترضي المستخدمين بما فيه الكفاية من خلال ارتفاع الوقت ، والفساد النادر أو الخسارة أو التعطل. في بعض الحالات ، كان التطبيق الذي يحتوي على عملية مراقبة لإعادة تشغيله إذا تعطلت مفيدة.

فيما يتعلق بـ RealLoc:

تحقق من قيمة الإرجاع من RealLoc - ضعها في متغير مؤقت. رعاية فقط إذا كان NULL إذا كان الحجم الجديد المطلوب هو> 0. في حالات أخرى ، ضعها في متغيرك غير المؤقت:

على سبيل المثال

    void* temp = realloc(m->data, m->max * sizeof(void*));
    if (m->max!=0&&temp==NULL) { /* crash or return error */ }
    m->data =(void**)temp;

تعديل

تغيرت "معظم الحالات" إلى "الكثير من الحالات" في (1).

أدرك أنك قلت تفترض أنه "يمكن القيام بشيء ما" إذا كان لا يمكن تخصيص الذاكرة. لكن إدارة الذاكرة هي اعتبار عالمي للغاية (!).

هناك أيضًا خطأ آخر دقيق يمكن أن يأتي من RealLoc. تسرب الذاكرة القادم من المؤشر الفارغ الذي تم إرجاعه معروف إلى حد ما (ولكن نادرًا جدًا أن يتعثر عليه). كان لدي في برنامجي حادث تحطم مرة واحدة في حين جاء من مكالمة realloc. كان لدي بنية ديناميكية عدلت حجمها تلقائيًا باستخدام releloc يشبه هذا:

m->data = realloc(m->data, m->max * sizeof(void*)); 

كان الخطأ الذي ارتكبه هو عدم التحقق من M-> بحد أقصى == 0 ، الذي حرر منطقة الذاكرة. ومصنوع من مؤشر بيانات M-> واحد لا معنى له.

أعلم أنه خارج عن الموضوع ، لكن هذه كانت القضية الحقيقية الوحيدة التي واجهتها مع RealLoc.

لقد صادفت المشكلة. التكوين هو OS: Win7 (64) ؛ IDE: VS2013 ؛ Debug (Win32).
عندما عادت realloc الفارغة بسبب الذاكرة في.

1. تغيير خاصية المشروع ، لتمكين عناوين كبيرة.
2. شغل منصة الحلول الخاصة بي من Win32 إلى X64.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top