رضا پویا
رضا پویا
خواندن ۴ دقیقه·۲ سال پیش

پشته و انباره (The Stack and The Heap)

سلام

یکی از مفاهیم پایه ای که که از توسعه دهندگان Net. انتظار می رود که آن را درک کنند مفهوم پشته (Stack) و انباره (Heap) است. این مفاهیم تقریبا یکی از موارد ثابت مصاحبه‌های تخصصی مربوط به #C هستند. در اینجا سعی کردم به صورت ساده‌ این مورد رو توضیح بدهم.

قبل از شروع بحث باید این نکته رو بگویم که به صورت عملیاتی تشخیص اینکه کدام بخش از متغیرها درون پشته و کدام بخش درون انباره قرار می‌گیرند، سخت است؛ دلیل این سخت بودن این است که سازندگان فریمورک Net. به صورت عمدی می خواستند که جزئیات پیاده سازی مدیریت حافظه را از چشم توسعه‌دهندگان مخفی کنند تا توسعه‌دهندگان جدید برای کار با #C نیاز به یادگیری مفاهیم حافظه نداشته باشند و با سپردن وظیفه مدیریت حافظه به فریمورک، وقت خود را صرف توسعه کدهای مربوط به منطق تجاری کنند.

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

از پشته( Stack ) برای تخصیص حافظه به صورت ایستا (Static)، و از انباره (Heap) برای تخصیص به صورت پویا (Dynamic) استفاده می‌گردد. هر دوی اینها در عمل درون RAM کامپیوتر قرار دارند.

ساختار حافظه برنامه Net.
ساختار حافظه برنامه Net.


پشته ( Stack ) :

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

همانطور که می دانید پشته ها از ساختار « LIFO » استفاده می کنند.LIFO کوتاه‌شدهٔ عبارت Last In First Out « آخرین ورودی از همه زودتر خارج می‌شود » است. این سیاست اساس کار پشته‌ها را تشکیل می‌دهد و به مفهوم آن است که آخرین دادهٔ ذخیره شده در پشته، نخستین داده‌ای است که بازیابی می‌شود.

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

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

همانطور که گفتیم، پشته به صورت LIFO هست، جدیدترین بلوک گرفته شده اولین بلوکی هست که آزاد می شود، این موضوع باعث می شود که نگه داشتن وضعیت پشته آسان بود. آزاد کردن یک بلوک از پشته به آسانی جابه‌جا کردن یک نشانگر ( Pointer ) است.

شکل شماتیک حافظه پشته و انباره  ، پشته از بلوک های در کنار هم تشکیل شده ولی انباره از فریم های جدا از یکدیگر
شکل شماتیک حافظه پشته و انباره ، پشته از بلوک های در کنار هم تشکیل شده ولی انباره از فریم های جدا از یکدیگر


انباره ( heap ) :

انباره بلوکی از حافظه است که « اشیاء/ objects » درون آن قرار می گیرند.

هر زمان یک شی ساخته می شود، بلوکی از حافظه درون heap به آن تخصیص داده می گردد و یک reference به آن شی، برگشت داده می شود.

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

فریمورک Net. یک Runtime و یک Garbage Collector دارد که به صورت دوره ای اشیایی که درون heap هستند را - در صورتی که هیچ ارجاع زنده از آنها موجود نباشد - حذف می کند تا برنامه با کمبود حافظه مواجهه نگردد. یک شی به محض اینکه دیگر توسط شی زنده ای ارجاع داده نشود، آماده ی آزاد سازی توسط garbage collector خواهد شد.

نمونه های value-types ( و ارجاع به اشیاء ) در همان جایی که اعلان شده اند، قرار می گیرند. اگر یک نمونه به عنوان یک فیلد درون یک کلاس یا به عنوان یک آرایه تعریف شده باشند، نمونه درون heap قرار خواهد گرفت.

علاوه بر این، heap فیلدهای ایستا ( Static Field ) را نگه می دارد. بر خلاف اشیاء که در heap به آنها فضا تخصیص داده شده ( که می توانند garbage collect گردند ) این ها تا زمانی که Application Domain پابرجاست، درون حافظه باقی می مانند.

در اینجا عملیات لازم بین حافظه ها و نحوه کار کردن پشته و انباره را می بینید، علاوه بر این مثالی از Boxing و UnBoxing را هم می بینید.
در اینجا عملیات لازم بین حافظه ها و نحوه کار کردن پشته و انباره را می بینید، علاوه بر این مثالی از Boxing و UnBoxing را هم می بینید.


محیط های چند نخی Multi Threaded

در وضعیتی که برنامه شما به صورت چند نخی ( Multi Thread ) اجرا شود، هر نخ « پشته » اختصاصی خود را دارد ولی « انباره » بین تمامی Thread ها به صورت مشترک است .

پشته مختص به Thread است ولی Heap مختص به خود Application است.

پشته برای مدیریت استثناء‌ها و اجرای Thread ها مهم و حیاتی است.


خب، امیدوارم که توضیحات مناسبی داده باشم. اگر خطایی و اشتباهی وجود داشت ممنون می شم من رو از این موضوع مطلع کنیم.



منابع

C# 9.0 in a Nutshell By Joseph Albahari

http://net-informations.com/faq/net/stack-heap.htm


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