هر برنامه برای اجرا به حافظه نیاز دارد.
حافظه مورد نیاز شامل محلی برای نگه داری دستورالعملهای برنامه و همچنین فضایی برای نگهداری داده هایی است که مورد پردازش قرار میگیرند.
داده ها ممکن است در قالب متغیرهایی ثابت در خود برنامه گنجانده شوند یا از منبعی دیگر (مثل کیبورد، فایل و غیره) وارد شوند.
تمامی کامپایلرها و سیستم های زمان اجرا (Run-Time) از داده با حجم پویا (dynamic) استفاده میکنند.
اندازه این داده ها از قبل مشخص نیست و مقدار فضای مورد نیاز آن را در زمان اجرا مشخص میشود.
به هر صورت هر برنامه باید فضای مورد نیازش تامین شود تا اینکه بتواند با سرعتی مناسب و به شیوه ای کارا اجرا شود.
به طور کلی سه نوع تخصیص حافظه داریم:
تخصیص حافظه ایستا (static memory allocatiom) : این حافظه ای است که به متغیرهای استاتیک و سراسری تخصیص داده میشود. حافظه ای که به این متغیرها تخصیص داده میشود در طول اجرا ثابت است و تا پایان برنامه پس گرفته نمیشود.
تخصیص حافظه اتوماتیک (automatic memory allocation) : این حافظه ای است که به متغیرهای محلی و توابع داده میشود. حافظه مورد نیاز این نوع متغیرها هنگامی تخصیص داده میشود که تابع مورد نظر اجرا شود (در صورتی که تابع مورد نظر اجرا نشود حافظه ای به آن اختصاص داده نمیشود.) هنگامی هم که تابع مورد نظر اجرایش خاتمه یابد حافظه اختصاص یافته به آن آزاد میشود و به سیستم برگردانده میشود.
تخصیص حافظه پویا (dynamic memory allocation) : این حافظه ای است که در حین اجرای برنامه، توسط خود برنامه درخواست میشود و به آن اختصاص داده میشود و باید توسط خود برنامه نیز آزاد شود در غیر اینصورت کماکان در اختیار خواهد بود.
تفاوت اینگونه تخصیص حافظه با حالت اتوماتیک این است که کامپایلر در حالت اتوماتیک مسئول واگذاری حافظه است و این کار در برنامه به دستورالعمل خاصی نیاز ندارد درحالی که در مورد تخصیص پویا برنامه بصورت صریح (explicit) درخواست تخصیص حافظه میدهد (برای مثال توسط فراخوانی عملگر new)
در هر دو حالت تخصیص ایستا و اتوماتیک، اندازه متغیر یا آرایه باید موقع کامپایل برنامه مشخص باشد.
در خیلی از موارد این شیوه تخصیص حافظه مشکلی ایجاد نمیکند اما موقعیت هایی وجود دارد که در زمان کامپایل امکان دانستن حافظه مورد نیاز امکان پذیر نیست مخصوصا هنگامی که برنامه با منبع داده بیرونی مانند فایل روی دیسک و یا شبکه سروکار دارد.
ممکن است قبل از اجرای برنامه اطلاعی از شمار رکوردهای ذخیره شده در فایل ورودی نداشته باشیم یا اینکه چند کاربر اینترنتی همزمان ممکن است به یک سرور لاگین کنند قابل پیش بینی نباشد.
میتوان حد بالایی را برای این منظور در نظر گرفت اما این باعث اتلاف حافظه میشود و از مقبولیت و کارایی برنامه میکاهد. از طرفی دیگر، متغیرهای محلی که از راه تخصیص اتوماتیک در حافظه جای داده میشوند در قسمتی از حافظه قرار داده میشوند که به آن پشته یا stack میگویند. اندازه پشتهstack در پیاده سازی های معمول کامپایلرها حداکثر 2 مگابایت است. اگر متغیر یا آرایه ای بزرگ بطور محلی (و یا تابعی) تعریف شود که حافظه مورد نیاز آن از اندازه پشته فراتر رود باعث خطا و توقف برنامه میشود. برای مثال دستور زیر در بیشتر سیستمها باعث خطای موقع اجرا و خرابی برنامه میشود.
int main()
{
int array[1000000]; // allocate 1 million integers (4MB of memory)
}
برای رفع این مشکل، مکانیزم تخصیص پویا در نظر گرفته شده است که در ادامه به تفصیل آن میپردازیم.
هنگام شروع یک برنامه ، اکثر سیستم عامل ها چهار بخش حافظه زیر را برای آن برنامه اختصاص می دهند:
در قسمت اول، دستورالعمل های برنامه واقع شده است (program instructions) => دستور العمل های ماشینی
قسمت دوم شامل حافظه تخصیص یافته به متغیرهای سراسری و ایستاست. فضای استفاده شده توسط این دو قسمت در طول اجرای برنامه ثابت است و تغییری نمیکند.
قسمت سوم: در قسمت stack حافظه به توابعی که اجرا میشوند داده میشود. به هر تابعی که فراخوانی میشود در بالای پشته فضایی جهت نگهداری متغیرهای محلی آن داده میشود(به علاوه آرگومان های ورودی توابع و مقدار خروجی آن ها). این تابع خود میتواند توابعی دیگر را فراخوانی کند و بدین ترتیب تابعی دیگر در بالای پشته قرار میگیرد.
مکانی در پشته برای تابع mainتخصیص داده شده است. به فضایی در پشته که به یک تابع تخصیص داده میشود یک Stack Frame گفته میشود.
چون تابع main در بدنه خود تابعf را فراخوانی کرده است، یک frame stack نیز به تابع f در بالای پشته داده شده است.
ادامه مطالب را به بخش های بعدی تخصیص خواهیم داد.
موفق باشید.
منبع: Modern Compiler Design by Dick Grune, Kees van Reeuwijk, Henri E. Bal, Ceriel J.H. Jacobs, Koen Langendoen