کمبود حافظه

از ویکی‌پدیا، دانشنامهٔ آزاد
پرش به: ناوبری، جستجو
Translation arrow fa.svg این نوشتار یک ترجمه از ویکی‌پدیاهای دیگر است. پيوند به سایر زبان‌ها را در سمت راست-پايين اين صفحه ببينيد.

کمبود حافظه ( در علوم کامپیوتر ) زمانی اتفاق می‌افتد که یک برنامه، حافظه ای از سیستم عامل دریافت کند ولی قادر به آزاد کردن و برگرداندن آن نباشد. کمبود حافظه خود نشانه‌ای بر وجود مشکلات دیگری (در ادامه می‌بینیم) است و معمولاً تنها با اشکال زدایی کد منبع توسط برنامه‌نویس اصلاح می‌شود. با این وجود بسیاری از مردم کمبود حافظه را به هر افزایش ناخواسته فضای مصرف شده نسبت می‌دهند، هرچند که این تعریف کاملاً درست نیست.

نتیجه[ویرایش]

کمبود حافظه با کاهش دادن مقدار فضای آزاد می‌تواند باعث کاهش کارایی سیستم گردد. سرانجام در بدترین حالت بیشتر فضای موجود اشغال می‌شود که این امر می‌تواند سبب از کار افتادن کل یا بخشی از سیستم و سخت افزار و بروز مشکل در نرم‌افزارها و یا حتی تاخیر غیر قابل قبولی در سیستم که منجر به تریشینک می‌شود، شود.

در استفاده‌های معمولی کمبود حافظه شاید خیلی جدی و قابل احساس نباشد. در سیستم عامل‌های جدید، حافظه مورد استفاده نرم‌افزارها پس از خروج آنها آزاد می‌گردد. یعنی کمبود حافظه در برنامه‌هایی که برای مدت کوتاهی اجرا می‌شوند خیلی جدی و قابل احساس نیستند.

بعضی از مواردی که کمبود حافظه می‌تواند جدی باشد:

  • زمانی که برنامه‌ای برای مدتی طولانی اجرا می‌گردد و در طول زمان فضای بیشتری مصرف می‌کند، مانند برنامه‌هایی که در پس زمینه سرورها مخصوصاً در سامانه های توکار که برای سالها در حالت اجرا باقی می‌مانند.
  • زمانی که حافظه‌ای به طور متناوب از سیستم گرفته می‌شود، مثلاً رمانی که فریم‌های یک بازی کامپیوتری و یا فیلم را تحلیل می‌کنیم.
  • زمانی که برنامه قادر به درخواست حافظه (مثلاً حافظه به اشتراک گذاشته شده) می‌باشد ولی حافظه حتی پس از خروج از برنامه آزاد نمی شود.
  • زمانی که کمبود به خاطر خود سیستم عامل به وجود آید.
  • زمانی که کمبود در راه‌انداز سخت افزار سیستم به وجود آید.
  • زمانی که حافظه خیلی محدود است مثلاً در سامانه های توکار یا دستگاههای قابل حمل.
  • زمانی که سیستم عامل پس از بسته شدن برنامه به طور خودکار حافظه‌های اخذ شده را آزاد نکند (و فقط پس از راه اندازی مجدد آزاد گردد)، مانند آمیگا او اِس.

مثالی از کمبود حافظه[ویرایش]

در مثال بیان شده (در زیر) که توسط شبه‌کد نوشته شده است قصد داریم نشان دهیم که کمبود حافظه چگونه می‌تواند بوجود آید و چگونه اثر می‌گذارد (بدون نیاز به هیچ گونه دانش برنامه نویسی). این برنامه قسمتی از یک نرم‌افزار ساده‌ای است که به منظور کنترل یک آسانسور نوشته شده است. این قسمت از برنامه زمانی که هر شخص داخل آسانسور دکمه مربوط به طبقه همکف فشار دهد، اجرا می‌شود.

زمانی که یک دکمه فشار داده شد
  فضایی برای ذخیره شماره طبقه اخذ کن
  شماره طبقه را در حافظه گرفته شده ذخیره کن
  آیا هم اکنون در طبقه همکف هستیم؟
  اگر آره:پس هیچ کاری انجام نده : تمام
  در غیر  اینصورت:
    منتظر بایست تا آسانسور آماده کار شود
    به طبقه مطلوب برو
    فضای استفاده شده برای ذخیره شماره طبقه را آزاد کن

در این مثال کمبود حافظه زمانی اتفاق می‌افتد که آسانسور در طبقه درخواست شده باشد. چون در این حالت فضای گرفته شده آزاد نمی‌گردد. هر بار که این عمل تکرار شود حافظه بیشتری گرفته می‌شود.

مواردی شبیه این مثال معمولاً بلافاصله تاثیر نمی‌گذارند. مردم معمولاً کلید مربوط به طبقه‌ای را که در آن هستند فشار نمی‌دهند، در هر حال ممکن است که آسانسور فضای گسسته کافی برای صد و یا هزار بار تکرار این پیش آمد را داشته باشد. با این وجود سرانجام فضای حافظه آن پر می‌شود. این کار می‌تواند ماه‌ها یا سالها طول بکشد، پس این مشکل از طریق آزمایش قابل تشخیص نیست.

نتیجه می‌تواند ناخوشایند باشد: زمانی که حافظه آزاد بسیار کم شود آسانسور نمی‌تواند پاسخی به درخواستها بدهد. اگر قسمتهای دیگر برنامه نیازی به فضا داشته یاشد مثلاً قسمتی که برای باز یا بسته کردن آسانسور نوشته شده است از آنجایی که دیگر نرم‌افزار قادر به بازکردن در نمی‌باشد سبب گیر کردن فرد در آسانسور شود.

کمبود حافظه تا زمانی که سیستم دوباره راه اندازی شود باقی می‌ماند. برای مثال اگر منبع آسانسور قطع گردد برنامه متوقف می‌شود. اگر منبع را دوباره وصل کنیم برنامه از اول اجرا می‌شود و فضای حافظه آزاد می‌شود ولی دوباره کم کم می‌تواند سبب کمبود حافظه گردد و سرانجام سبب لطمه به اجرای صحیح سیستم گردد.

موضوع برنامه نویسی[ویرایش]

کمبود حافظه یک خطای معمول در برنامه‌نویسی محسوب می‌شود، مخصوصاً زمانی که از زبانی که در داخل خودش زباله روبی خودکار ندارد استفاده می‌کنیم مانند سی++ یا سی. معمولاً به این علت کمبود حافظه رخ می‌دهد که حافظه پویا به حافظه غیر قابل دسترس تبدیل می‌شود. رواج مشکل کمبود حافظه سبب تولید تعداد زیادی ابزار اشکال‌زدایی که قابلیت تشخیص حافظه غیر قابل دسترس را داشتند شد. قابلیت زباله روبی "محافظه کار" را می‌توان به صورت یک ویژگی داخلی به هر زبانی که آن را ندارد اضافه نمود. کتابخانه هایی برای انجام این کار برای سی و سی++ موجود است. زباله روب محافظه کار قابلیت پیدا کردن و آزاد سازی بیشتر حافظه‌های غیر قابل دسترس را دارند نه همه.

مدیریت حافظه می‌تواند حافظه‌های غیر قابل دسترس را بازیابی کند، (نمی‌تواند حافظه‌هایی که در در دسترس هستند را آزاد کند) که این امر ذاتاً مفید می‌باشد. مدیریت حافظه‌های مدرن، تکنونوژی به برنامه نویسان ارائه کردند تا با کمک آن بتوانند براساس تفاوت سطحهای مفید بودن نشانه گذاری کنند (تفاوت در سطحهای قابل دسترس بودن). مدیریت حافظه شئ‌هایی را که به طور قوی در دسترس هستند را پاک نمی‌کنند. یک شئ را زمانی در دسترس قوی می‌نامیم که توسط یک ارجاع قوی در دسترس باشد یا توسط زنجیره‌ای از ارجاع‌های قوی در دسترس باشد. (زمانی یک ارجاع را ارجاع قوی می‌نامیم که بر خلاف ارجاع ضعیف از زباله روب شدن خودداری کند). بدین منظور توسعه دهندگان مسئول پاک‌سازی ارجاع‌ها پس از استفاده از آنها می‌باشند که این امر معمولاً با مقدار دهی تهی به ارجاع‌هایی که احتیاج ندارند صورت می‌گیرد و اگر لازم باشد با پاک کردن هر گوش دهنده‌ی رخدادی که شامل ارجاع قوی به شئ مورد نظر باشد صورت می‌گیرد.

مدیریت حافظه خودکار برای توسعه دهندگان بسیار مناسب می‌باشد، زیرا آنها احتیاجی به پیاده سازی فرایند آزاد سازی حافظه ندارند و هیچ گونه نگرانی برای اینکه آیا حافظه به خوبی آزاد شده است یا نه ندارد و دیگر نیازی به تمرکز بر اینکه آیا برای حافظه ارجاعی وجود دارد یا نه ندارند. برای برنامه نویسان دانستن اینکه نیازی به ارجاع ندارند راحتنر از این است که بدانند چه زمانی به حافظه هیچ ارجاعی وجود ندارد. با این وجود مدیریت حافظه خودکار باعت کاهش سرعت اجرا می‌شود و نمی‌تواند همه خطاهایی که منجر به کمبود حافظه می‌شود را حذف کند.

آر اِی آی آی[ویرایش]

آر اِی آی آی (فراگیری منابع به صورت ارزش دهی اولیه) روشی است که معمولاً در سی++، دی و ایدا برای این مشکل استفاده می شود. در این روش، منابع به شئ هایی که در بلوکها قرار دارند نسبت داده می‌شوند و به محض اینکه شئ از بلوک خارج شد به طور خودکار منابع آزاد می‌شود. بر خلاف زباله روبها، آر اِی آی آی این خوبی را دارد که می دانیم که شئ چه زمانی وجود دارد و چه زمانی وجود ندارد. حال این کد سی را با سی++ مقایسه می کنیم:

/* C version */
#include <stdlib.h>
 
void f(int n)
{
  int* array = calloc(n, sizeof(int));
  do_some_work();
  free(array);
}
// C++ version
#include <vector>
 
void f(int n)
{
  std::vector<int> array (n);
  do_some_work();
}

در نسخه سی، نیاز به آزادسازی به صورت واضح(مستقیم) وجود دارد (آرایه در Heap تخصیص داده می‌شود و تا وقتی که به صورت واضح آزاد نشود در Heap باقی می‌ماند.) در نسخه سی++ نیازی به آزادسازی به صورت واضح وجود ندارد و این امر به طور خودکار زمانی که از بلوک خارج شد صورت می‌گیرد و نیز شامل زمانی که استثنا رخ می‌دهد می‌شود. این امر بر روی منابع دیگری به جز حافظه قابل اجرا می‌باشد، از قبیل:

  • دستگیره‌ی فایلها (که زباله روب "علامت و پیچ و خم" نمی‌تواند به خوبی کنترل کند)
  • پنجره‌هایی که باید بسته باشند
  • آیکون‌هایی که باید در نوار اطلاع باید مخفی باشند
  • اتصالات شبکه
  • شئ‌های جی دی آی ویندوز

با این وجود استفاده صحیح از آر اِی آی آی همیشه ساده نیست و مشکلات مربوط به خودش را دارد. برای مثال اگر شخص مراقب نباشد، ممکن است که با برگرداندن داده‌ای که با خروج از بلوک آزاد می‌گردد به صورت ارجاع، باعث به وجود آمدن اشاره گر آویزان شود.

زبان دی از ترکیبی از آر اِی آی آی و زباله روب استفاده می‌کند (استفاده از مخرب خودکار زمانی که شئ به طور واضح از بلوک خارج می‌شود و استفاده از زباله روب در غیر این صورت).

شمارنده ارجاع و مرجع های چرخه ای[ویرایش]

طراحی زوباله روب‌های جدید معمولاً بر اساس در دسترس بودن می‌باشد (اگر هیج ارجاع قابل استفاده‌ای به آن موجود نیست می‌توان آن را جمع آوری کرد). دسته‌ای دیگر از زباله روب ها بر اساس شمارنده ارجاع می‌باشد به این صورت که هر شی مسئول نگه‌داری رد مسیر برای دانستن تعداد ارجاع‌ها به آن می‌باشد. اگر تعداد برابر صفر شود از شئ انتظار می رود که خودش را آزاد کند.مشکل این طرح این که نمی‌تواند در مرجع‌های چرخه‌ای (دایره ای) به خوبی رفتار کند و به همین دلیل امروزه ما برای پذیرفتن هزینه‌ی زیاد سیستم‌های نشانه‌گذاری و زدودن آماده می‌شویم.

در مثال زیر مشکل کمبود حافظه در شمارنده ارجاع به تصویر کشیده شده است:

Dim A, B
Set A = CreateObject("Some.Thing")
Set B = CreateObject("Some.Thing")
' At this point, the two objects each have one reference,
Set A.member = B
Set B.member = A
' Now they each have two references. 
Set A = Nothing
' You could still get out of it...
Set B = Nothing
' You now have a memory leak.

در این مثال کوچک می‌توان به راحتی مشکل را بر طرف کرد ولی در اکثر مثالهای واقعی، چرخه‌ی اشاره گرها بیش از دو شئ را در بر گرفته و تشخیص آن سخت تر است.

یک مثال معروف از این نوع کمبود حافظه، در زبان آژاکس در مرورگرهای وب می‌باشد.

تاثیرات[ویرایش]

اگر یک برنامه کمبود حافظه داشته باشد و حافظه‌ی مورد استفاده آن به طور یکنواخت افزایش یابد، هیچ نشانه سریعی (سریعاً قابل تشخیص باشد) قابل مشاهده نیست. هر سیستم یک نهایت برای حافظه دارد و با دوباره اجرا کردن برنامه به زودی و یا پس از مدتی مشکل تکرار می‌شود.

اکثر استفاده کننده‌های سیستم عامل‌های رو میزی از حافظه اصلی(بر روی میکروچیپ های رَم) و هم از حافظه ثانویه ( مانند دیسک سخت) استفاده می‌کنند. تخصیص حافظه به صورت پویا صورت می‌گیرد (هر عمل به اندازه‌ای که نیاز دارد). صفحه‌های فعال برای دسترسی سریع به حافظه‌ی اصلی انتقال می‌یابند و صفحه‌های غیر فعال برای صرفه جویی در حافظه به حافظه ثانویه انتقال می‌یابند. زمانی که یک پروسه شروع به مصرف زیاد حافظه می‌کند معمولاً موفق به اخذ حافظه بیشتر و بیشتر می‌شود و باعث فرستادن برنامه‌های دیگر به حافظه‌ی ثانویه می‌گردد (که باعث هزینه‌ی زیاد و سرعت کم). حتی اگر برنامه‌ای که سبب کمبود حافظه شده است خاتمه داده شود ممکن است مدت زمانی برای برنامه‌های دیگر طول بکشد تا خود را به حافظه اصلی انتقال دهند و کارایی به حالت عادی خود برسد.

زمانی که همه‌ی حافظه یک سیستم مصرف شد (زمانی که حافظه مجازی وجود داشته باشد یا فقط حافظه اصلی موجود باشد مانند سیستم‌های جاسازی شده) هر گونه تلاش برای تخصیص حافظه رد می‌شود. این امر سبب می‌شود که برنامه برای خاتمه دادن به خود، شروع به تلاش برای تخصیص حافظه کند یا شروع به تولید قطعه بندی خطا کند. بعضی از برنامه‌ها برای بازیابی از چنین شرایطی طراحی شده‌اند (ممکن است با استفاده از حافظه از قبل رزرو شده). اولین برنامه‌ای با پر شدن حافظه روبه رو می‌شود ممکن است آن برنامه‌ای نباشد که دارای کمبود حافظه است.

بعضی از سیستم عامل‌های چند وظیف ای یک روش مخصوص به خود برای برخورد با پر شدن حافظه دارند مانند حذف تصادفی پروسه‌ها (ممکن است بر روی یک پروسه بی گناه صورت بگیرد) و یا حذف پروسه‌ای که از بیشترین حافظه استفاده می‌کند (که احتمالاً یکی از پروسه‌هایی بوده که سبب مشکل بوده). بعضی از سیستم عامل‌ها برای هر پروسه یک محدودیت حافظه دارند تا از به زور گرفتن همه حافظه توسط برنامه جلوگیری کند. بدی این ایده این است که سیستم عامل باید گاهی اوقات دوباره تنظیم گردد تا بتواند به برنامه‌هایی که واقعاً نیاز به فضای زیادی دارند فضای کافی در نسبت دهد (مانند برنامه‌های گرافیکی، ویرایش فیلم و محاسبات علمی).

اگر کمبود حافظه در هسته سیستم عامل رخ دهد سیستم عامل متوقف می‌شود. کامپیوترهایی که از مدیریت حافظه خبره استفاده نمی‌کنند مانند سیستم‌های جا سازی شده ممکن است در اثر کمبود حافظه ماندگار، از کار متوقف شود.

سیستم‌های در دسترس عموم مانند سرویس دهنده های وب یا مسیریاب ها برای حملات انکار سرویس مستعد هستند در صورتی که حمله کننده دنباله از کارهایی که سبب کمبود حافظه می‌شود راکشف کند. مانند دنباله‌هایی که به اکسپلویت موسوم هستند.

افزایش دندانه ای

الگوی دندانه‌ای استفاده از حافظه ممکن است نشان دهنده‌ی کمبود حافظه باشد اگر پرشهای کاهشی عمودی همزمان با دوباره بوت کردن یا راه اندازی مجدد نرم‌افزار رخ دهد. البته باید مراقب بود زیرا زباله روب نیز ممکن است سبب تولید چنین الگویی شود.

دیگر مصرف کننده‌های حافظه[ویرایش]

باید توجه داشته باشیم که افزایش مداوم استفاده از حافظه نمی‌تواند نشان کمبود حافظه باشد. مثلاً بعضی از برنامه‌ها به طور افزایشی مقدار زیادی از اطلاعات را در حافظه ذخیره می‌کنند (شبیه ذخیره گاه). اگر این ذخیره گاه بتواند به مقدار زیادی افزایش یابد باعث ایجاد مشکل می‌گردد که ممکن است در اثر طراحی و یا برنامه نویسی نا درست بوجود آمده باشد، ولی این امر کمبود حافظه تلقی نمی‌شود زیرا تمام حافظه استفاده شده قابل دسترسی می‌باشد. در حالتی دیگر ممکن است برنامه نویس به طور نامعقولی درخواست حافظه‌ی زیادی کند چون که برنامه نویس در این تصور بوده که حافظه موجود برای آن کار همیشه در دسترس است. برای مثال پردازش گر فایل‌های گرافیکی شروع به خواندن همه‌ی فایل و ذخیره آن در حافظه کند، در صورتی که فضای لازم موجود نباشد برنامه در اجرا دچار مشکل می‌شود.

کمبود حافظه در اثر نوع خاصی از اشکال در برنامه نویسی رخ می‌دهد و در صورتی به منبع کد آن دسترسی نداشته باشیم تنها با دیدن نشانه‌های آن می‌توان حدس زد که شاید بر نامه دارای کمبود حافظه می‌باشد. در صورتی که به درون برنامه هیچ شناختی نداریم بهتر است از عبارت "افزایش مداوم و ثابت حافظه مورد استفاده" استفاده کنیم.

مثالی ساده در زبان سی[ویرایش]

در این مثال سی ما عمداً با از دست دادن اشاره گر به حافظه تخصیص یافته سبب کمبود حافظه می‌شویم. به این دلیل که تخصیص حافظه بدون ذخیره آدرس آن به طور همیشگی ادامه دارد در نهایت باعت به وجود آمدن توقف (مقدار تهی بر می‌گرداند) در اجرا می‌شود در جایی که حافظه پر شده است. به این دلیل که آدرس حافظه تخصیص یافته ذخیره نمی‌شود امکال آزاد کردن حافظه‌های تخصیص یافته قبلی وجود ندارد.

باید به نکته توجه داشت که سیستم عامل تا زمانی که هیچ داده‌ای بر روی حافظه تخصیص یافته نوشته نشده است بین تخصیص حافظه واقعی تاخیر می‌اندازد و برنامه زمانی که آدرس‌های مجازی آن از محدوده خارج می‌شود پایان می‌یابد. (برای هر پروسه بین 2 تا 4 گیگا بایت محدود است البته برای سیستم عامل‌های 64 بیتی محدودیت کمتری وجود دارد).

#include <stdlib.h>
 
int main(void)
{
     /* this is an infinite loop calling the malloc function which
      * allocates the memory but without saving the address of the
      * allocated place */
     while (malloc(50)); /* malloc will return NULL sooner or later, due to lack of memory */
     return 0;  /* free the allocated memory by operating system itself after program exits */
}

منابع[ویرایش]

  • ویکی‌پدیای انگلیسی. //en.wikipedia.org/wiki/Memory_leak