سؤال

لدي تطبيق حي طويل مع تخصيص تخصيص الذاكرة المتكررة. هل سيعود أي تطبيق تنفيذ malloc الذاكرة إلى النظام؟

ما هو، في هذا الصدد، سلوك:

  • ptmalloc 1، 2 (GLIBC الافتراضي) أو 3
  • dlmalloc.
  • TCMalloc (Google Threaded Malloc)
  • سولاريس 10-11 افتراضي malloc و mtmalloc
  • FreeBSD 8 افتراضي Malloc (Jemalloc)
  • الكنز مالوك؟

تحديث

إذا كان لدي تطبيق يمكن أن يكون استهلاك الذاكرة مختلفا تماما في النهار والايت في الليل (على سبيل المثال)، هل يمكنني إجبار أي من malloc للعودة ذاكرة تحريرها إلى النظام؟

بدون هذه العائدات سيتم تبديل الذاكرة في كثير من الأحيان، ولكن هذه الذاكرة تحتوي فقط على القمامة فقط.

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

المحلول

ينطبق التحليل التالي فقط على GLIBC (بناء على خوارزمية PTMALLOC2). هناك خيارات معينة تبدو مفيدة لإرجاع الذاكرة المحررة مرة أخرى إلى النظام:

  1. مولوبت () (المعرفة في malloc.h) تقدم خيارا لضبط قيمة Trim Treshold باستخدام أحد خيار المعلمة M_TRIM_THRESHOLD, ، يشير هذا إلى الحد الأدنى من الذاكرة الحرة (بالبايت) المسموح به في الجزء العلوي من قطاع البيانات. إذا كان المبلغ يقع تحت هذا العتبة، فإن Glibc يستدعي brk() لإعادة الذاكرة إلى النواة.

    القيمة الافتراضية لل M_TRIM_THRESHOLD في Linux، تم ضبط Linux على 128 ألف، مما يؤدي إلى توفير قيمة أصغر مساحة.

    يمكن تحقيق نفس السلوك عن طريق تحديد قيمة تقليم العتبة في متغير البيئة MALLOC_TRIM_THRESHOLD_, ، مع عدم وجود مصدر مصدر بالتأكيد.

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

  2. من الممكن تقليم ساحة الذاكرة وإعطاء أي ذاكرة غير مستخدمة مرة أخرى إلى النظام عن طريق الاتصال malloc_trim(pad) (المعرفة في malloc.h). تعديل هذه الوظيفة من قطاع البيانات، وترك على الأقل pad بايت في نهاية الأمر والفشل إذا كان يمكن تحرير أقل من صفحات واحدة من البايتات. حجم القطاع هو دائما متعددة من صفحة واحدة، والتي تبلغ 4،096 بايت على i386.

    تنفيذ هذا السلوك المعدل free() استخدام malloc_trim يمكن القيام به باستخدام وظيفة ربط malloc. لن يتطلب هذا أي رمز مصدر على مكتبة Glibc الأساسية.

  3. استخدام madvise() نداء النظام داخل التنفيذ المجاني ل Glibc.

نصائح أخرى

معظم التطبيقات لا تهتم بتحديد تلك الحالات (النادرة نسبيا) حيث تم تحرير "كتل" (كتل "(أي حجم يناسب نظام التشغيل) ويمكن إرجاعها، ولكن هناك استثناءات بالطبع. على سبيل المثال، وأنا اقتبس من صفحة ويكيبيديا, ، في openbsd:

على مكالمة free, يتم إصدار الذاكرة وغير مستفادة من مساحة عنوان العملية باستخدام MunMap. تم تصميم هذا النظام لتحسين الأمان من خلال الاستفادة من ميزات علامات عشوائية تخطيط الفضاء والعنوان المطبقة كجزء من OpenBSD mmapاستدعاء النظام، والكشف عن الأخطاء بعد الظهر - كإجراء تخصيص لذاكرة كبيرة غير محددة تماما بعد إطلاق سراحه، فإن الاستخدام الإضافي يؤدي إلى خطأ تجزئة وإنهاء البرنامج.

معظم الأنظمة ليست كأمان يركز على أنها openbsd، رغم ذلك.

مع العلم بذلك، عندما أكون ترميز نظاما طويلا قيد التشغيل لديه متطلبات عابرة معروفة مقابل كمية كبيرة من الذاكرة، أحاول دائما fork العملية: الوالد ثم ينتظر فقط النتائج من الطفل [[عادة على أنبوب]]، يقوم الطفل بإرجاع الحساب (بما في ذلك تخصيص الذاكرة)، ويعزز النتائج [[على الأنابيب المذكورة]]، ثم ينتهي. وبهذه الطريقة، لن تكون عملية التشغيل الطويلة الطويلة لذاكرة الذاكرة بدون فائدة خلال الأوقات الطويلة بين "المسامير" في بعض الأحيان في طلبها على الذاكرة. تشمل الاستراتيجيات البديلة الأخرى التبديل إلى مخصص الذاكرة المخصصة لمثل هذه المتطلبات الخاصة (C ++ يجعلها سهلة بشكل معقول، على الرغم من أن اللغات مع الأجهزة الافتراضية الموجودة أسفل مثل Java و Python لا) عادة.

أنا أتعامل مع نفس المشكلة مثل المرجع. حتى الآن، يبدو من الممكن مع TCMalloc. لقد وجدت حلولين:

  1. ترجمة البرنامج الخاص بك مع TCMalloc مرتبط، ثم قم بتشغيله على النحو التالي:

    env TCMALLOC_RELEASE=100 ./my_pthread_soft
    

    وثائق يذكر ذلك

    معدلات معقولة في النطاق [0،10].

    ولكن 10 لا يبدو كافيا بالنسبة لي (أي لا أشاهد أي تغيير).

  2. ابحث في مكان ما في التعليمات البرمجية حيث سيكون من المثير للاهتمام إصدار جميع الذاكرة المحررة، ثم أضف هذا الرمز:

    #include "google/malloc_extension_c.h" // C include
    #include "google/malloc_extension.h"   // C++ include
    
    /* ... */
    
    MallocExtension_ReleaseFreeMemory();
    

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

تلقيت مشكلة مماثلة في تطبيقي، بعد بعض التحقيق لاحظت أنه لسبب ما لم يعد Glibc الذاكرة إلى النظام عند تخصيص الكائنات الصغيرة (في حالتي التي تقل عن 120 بايت).
انظر إلى هذا الرمز:

#include <list>
#include <malloc.h>

template<size_t s> class x{char x[s];};

int main(int argc,char** argv){
    typedef x<100> X;

    std::list<X> lx;
    for(size_t i = 0; i < 500000;++i){
        lx.push_back(X());
    }

    lx.clear();
    malloc_stats();

    return 0;
}

إخراج البرنامج:

Arena 0:
system bytes     =   64069632
in use bytes     =          0
Total (incl. mmap):
system bytes     =   64069632
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

حوالي 64 ميغابايت لا تعود إلى النظام. عندما غيرت typedef إلى:typedef x<110> X; يشبه إخراج البرنامج:

Arena 0:
system bytes     =     135168
in use bytes     =          0
Total (incl. mmap):
system bytes     =     135168
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

تم تحرير جميع الذاكرة تقريبا. لاحظت أيضا أن استخدام malloc_trim(0) في كلتا الحالتين صدر الذاكرة إلى النظام.
هنا هو الإخراج بعد إضافة malloc_trim إلى الرمز أعلاه:

Arena 0:
system bytes     =       4096
in use bytes     =          0
Total (incl. mmap):
system bytes     =       4096
in use bytes     =          0
max mmap regions =          0
max mmap bytes   =          0

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

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

الجواب القصير: لفرض النظام الفرعي Malloc لإرجاع الذاكرة إلى نظام التشغيل، استخدم Malloc_trim (). خلاف ذلك، فإن سلوك الذاكرة العائدين يعتمد على التنفيذ.

FreeBSD 12'S. malloc(3) الاستخدامات jemalloc. 5.1، التي ترجع الذاكرة المحرة ("الصفحات القذرة") إلى نظام التشغيل باستخدام madvise(...MADV_FREE).

يتم إرجاع الذاكرة المحررة فقط بعد تأخير الوقت الذي يتم التحكم فيه opt.dirty_decay_ms و opt.muzzy_decay_ms; ؛ انظر صفحة يدوية وهذه القضية على تنفيذ تطهير الدورة القذرة غير المستخدمة لمزيد من التفاصيل.

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

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