اینم یه تجربه پراکنده دیگه!
چند شب پیش یکی از دوستان با این سوال به سراغ من اومد:
مشکل اینه که وقتی با malloc یا new یک حافظه تخصیص میدی و با free یا delete حافظه رو برمیگیردونی, اون حافظه شاید بلاک هاش خالی بشه اما توی تسک لیست خالی به عنوان منابع استفاده شده میاره و تا پایان پروسه پاک نمیشه. از دوستانی که آشنایی دارن پیگیری کن ببین جریان چیه. مهمه .
خدا خیرت بده. یه جایی از حافظه نمایی رشد میکه و وقتی خالیش میکنیم (با اینکه از لیست heap سیستم عامل خالی میشه) اما توی لیست پروسهها(مثل نتیجه ps) هنوز به عنوان حافظه استفاده شده هست و حافظه آزاد نمیشه !!! عجیبه . کلی توی اینترنت اینو سوال کردن !!
من که لینوکسم. اما فکر کنم ویندوزم همینه. چون سوالای اینترنت هردوشونه
کلا بخوام سوال رو خلاصه کنم اینجوریه که چرا وقتی حافظه رو به سیستمعامل پس میدیم واقعا پس داده نمیشه. من یکم از دانستههام استفاده کردم و یکم هم تحقیقات کردم
پیش نیازهایی که به نظرم باید بدونیم ایناست:
-
نکته صفر اینکه با توجه به تحقیقات من زیر کلمه کلیدی new از همون malloc استفاده میشه. پس بررسی malloc به تنهایی میتونه جوابی برای ما فراهم کنه.
-
اول اینکه باید بدونیم که وقتی malloc صدا زده میشه چه اتفاقی میفته. کاری که malloc میکنه اینه که heap پروسه رو بهت اختصاص میده. حالا heap توسط سیستم عامل مدیریت میشه و در صورت نیاز به پروسهی شما حافظه بیشتری تخصیص میده.
-
دوم اینکه فضای heap توسط کرنل مدیریت میشه و به اصطلاح نگاشته میشه به یه چیزی به نام Virtual Memory
-
سوم اینکهVirtual Memory نگاشته میشن به چیزی به نام Memory Page که نزدیک به سخت افزار تره
-
چهارم اینکه Memory Page نگاشته میشن بلوکهای واقعی حافظه. از این مطمئن نیستم اما این نزدکترین لایه به سخت افزاره که من باهاش آشنا هستم.
حالا اگه بخوایم مساله رو بررسی کنیم باید به چندتا سوال جواب بدیم:
- اول اینکه malloc چطور عمل میکنه؟ در پیادهسازیش ساز و کارش چیه؟
- دوم اینکه روند افزایش طول Virtual Memory و اضافه شدن Memory Pageها چطوره؟
- سوم اینکه بعد از پس دادن Memory Pageها سیستم عامل چطور اونها رو از یک پروسه پس میگیره؟
خب جواب سوالا به ترتیب اینا به نظر میرسن:
-
پیادهسازیهای متفاوتی از malloc وجود داره
- پیادهسازی معمولا اون اینطوریه که پس از گرفتن حافظه وقتی free شد معمولا اونها رو به سیستم عامل پس نمیده و نگه میداره که شاید بعدا بخواد دوباره تخصیص بده.
- دوم اینکه درخواست یک Memory Page جدید توسط دستوراتی مثل mmap و sbrk درخواست یک بلاک بزرگ حافظه میکنه و اون رو بنا به درخواست نرمافزار تخصیص میده.
- بعدش هم اینکه برای اینکه یک Memory Page رو برگردونه به سیستم عامل بایستی تمام حافظههایی که روی اون تخصیص داده شدن برگشت داده بشه. که این هم همیشه اتفاق نمیافته.
- معمولا هم پیادهسازیها اینجورین که حافظهای که توسط sbrk افزایش پیدا کرده رو دست نمیزنن(دلیل دقیقش رو نمیدونم) و حافظهای که توسط mmap اضافه شده رو به راحتی برمیگردونن.
-
تقریبا جواب سوال دوم رو هم تا الان دادم. مقدار Virtual Memory توسط glibc کنترل میشه و بوسیله malloc تخصیص داده میشه.
-
جواب سوال سوم هم اینه که کرنل لینوکس همیشه حافظه برگشت داده شده رو برنمیگردونه چون که هزینه داره فقط اون رو علامت میزنه که در صورت نیاز اون رو حذف کنه یا به swap ببره.
حالا بریم سراغ مساله اصلی(چرا حافظه برگشت داده شده برگشت نخورده) و توجیهی که براش پیدا کردیم:
- مهمترین دلیل این اتفاق به نظرم malloc هست که حافظه رو نگه داشته و برنگردونده
- دومین دلیلش هم میتونه این باشه که کرنل هنوز Memory Page رو برنداشته و فقط علامت زده
حالا راه حل چیه:
- اول اینکه به کرنل و glibc اعتماد کنیم
- دوم اینکه بیایم و این پارامتر مروبط به malloc رو تنظیم کنیم4
- سوم اینکه بیایم از یه پیادهسازی دیگه برای تخصیص حافظه و آزاد سازی اون استفاده کنیم
- چهارم اینکه بیایم خودمون مستقم از mmap و munmap استفاده کنیم
امیدوارم اینا راه گشای دیگران هم باشه
برای مطالعه بیشتر:
همین!