یوشا آل ایوب

وبگاه دست نوشته ها و تجربیات شخصی

یوشا آل ایوب

وبگاه دست نوشته ها و تجربیات شخصی

یوشا آل ایوب

دکتر مصطفی چمران: می گویند تقوا از تخصص لازمتر است، آنرا می پذیرم، اما می گویم: آنکس که تخصص ندارد و کاری را می پذیرد، بی تقواست!

تبلیغات
Blog.ir بلاگ، رسانه متخصصین و اهل قلم، استفاده آسان از امکانات وبلاگ نویسی حرفه‌ای، در محیطی نوین، امن و پایدار bayanbox.ir صندوق بیان - تجربه‌ای متفاوت در نشر و نگهداری فایل‌ها، ۳ گیگا بایت فضای پیشرفته رایگان Bayan.ir - بیان، پیشرو در فناوری‌های فضای مجازی ایران
بایگانی

حافظه Heap و Stack

۹۴/۰۵/۲۳
  • مقدمه

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

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

و نهایتاً همه اینها در سلول های RAM سخت افزاری کامپیوتر بصورت منظم چیده میشن.

 

ram-stack-heap

 

فهرست/مندرجات:

  • حافظه Stack
    • قوائد حافظه Stack​
    • مشکل Stackoverflow
  • حافظه Heap
    • قوائد حافظه Heap
    • Garbage Collector / GC
    • مشکل Memory leak

 

  • حافظه Stack

حافظه Stack مثل دسته ای از بشقاب هستش که روی هم قرار گرفتن... هروقت بخواهید بشقابی روی دسته بشقاب ها قرار بدید(Push) معمولاً اون رو در بالا قرار میدید. به همین ترتیب هروقت بخواهید بشقابی از این دسته بردارید(Pop) همیشه بشقاب بالایی رو برمیدارید... که این دسته رو Stack هه LIFO یا همون LastIn-FirstOut می نامن.

 

 

حافظه Stack در قسمت user-space حافظه قرار داره و بصورت خودکار توسط CPU مدیریت میشه. این حافظه محل نگهداری متغیرهای محلی غیر استاتیک، پارامتر های توابع و آدرس های بازگشتی(return) توابع هست که بصورت LIFO)Last In First Out) داخل این حافظه بر روی هم انباشته میشن و به Frame معروفند. وقتی تابعی فراخونی و اجرا میشه، تابع، پارامترهاش و همه متغیرهای غیر static داخلیش در حافظه Stack قرار میگیرن. با فراخوانیه تابع جدید، این تابع روی تابع قبلی قرار میگیره و زمانی که کار یکی از این توابع تموم شد، اون تابع به همراه تمام متغیرهای مربوطه از داخل حافظه Stack خارج میشه... و کار به همین شکل ادامه پیدا می کنه.

نکته: نام دیگر این از نوع تخصیص حافظه، Static memory allocation یا اختصاص حافظه بصورت ایستا هستش.

نکته 2: حافظه ای که در Memory Firewall ها ازش صحبت میشه، همین قسمت Stack و Heap حافظه مجازی هستش و نه حافظه فیزیکی.

نکته 3: در بعضی زبانهای برنامه نویسی مثل Cpp قادرید شی رو در حافظه Stack هم ذخیره کنید، به شرط اینکه داخل تابع باشه و از new استفاده نکنید.

مثال:

function myFunction
   (param1, param2) // To Stack memory.
{
   _testVar = param1 + param2; // To Stack memory.
   return _testVar; // To Stack memory.
}

مثال تصویری:

 

  • قوائد حافظه Stack
  1. در حافظه مجازی دستگاه، قسمت Stack قرار داره.
  2. محل ذخیره متغیرهای محلی(غیر static)، پارامتر های توابع، آدرس های بازگشتی(return) توابع هست. (همچنین ردگیری توابع تودرتو)
  3. سایز Stack برنامه هنگام Compile تعیین و تخصیص داده میشه.
  4. در مقابل مشکل Stackoverflow پاسخگو هستش.
  5. بصورت خودکار توسط CPU مدیریت میشه.
  6. با فراخوانی تابع، Push میشه بداخل حافظه Stack و با خاتمه کار تابع، Pop میشه از داخل حافظه Stack.
  7. از نظر سرعت در اشغال فضا، از حافظه Heap سریعتره.
  8. در زبان C/Cpp، کلاس ها و ساختمان ها درصورت عدم استفاده از اشاره گر در حافظه Stack ذخیره میشن.
  9. در حین allocation/اشغال نابخردانه یا بیش از ظرفیت می تونه stackoverflow رخ بده.
  10. هر برنامه یک thread main داره و هر thread یک حافظه Stack خصوصی داره. پس تا وقتی که thread بسته نشده Stack ش هم وجود داره. (در اینجا بستن یعنی exit و نه terminate)
  11. حوزه ی حافظه Stack برنامه به thread اون برنامه ضمیمه شده.
  12. داده ها در حافظه Stack بترتیب بر روی هم قرار می گیرند.(با قائده LIFO)
  13. با افزایش مصرف حافظه stack، حافظه کمتری برای heap باقی می مونه.
  14. این نوع از حافظه readable و writable هستش.
  15. و...

 

 

  • مشکل Stackoverflow

واژه stackoverflow در لغت بمعنی سرریز شدن Stack هستش. از جمله دلایل خطای stackoverflow میشه عمق زیاد توابع تودرتو/nested و chain، متغیرهای محلی حجیم، بزرگ شدن بیش از حد سایز stack، تخریب یا corrupt شدن قسمتی از memory، استفاده اشتباه از Native API رو نام برد.

نکته: مشکل Stackoverflow از دسته Error ها هستش و نه Exception ها.

 

با اجرای کدهای زیر درک بیشتری نسبت به حافظه Stack و مشکل Stackoverflow پیدا خواهید کرد:

مثال PHP:

function myInfiniteRecursion()
{
   myInfiniteRecursion();
}

myInfiniteRecursion();

PHP Fatal error: Allowed memory size of 134217728 bytes(134MB) exhausted (tried to allocate 130968 bytes) in develop.php on line 5.

در این مثال با هربار فراخوانی تابع myInfiniteRecursion()، یک Stack Frame برای اون تابع در حافظه Stack ایجاد میشه و این روند تا جایی که موتور PHP (مقدار memory_limit در php.ini) اجازه داشته باشه ادامه پیدا میکنه... نهایتاً ظرفیت مجاز Stack موتور PHP پر میشه و اسکریپت سعی میکنه به خارج از ظرفیت Stack دسترسی پیدا کنه اما سیستم اجازه نمیده و اسکریپت رو با خطای stackoverflow متوقف میکنه.

 

مثال Java:

class MyClass
{
    private MyClass MyCls = new MyClass();

    public static void main(String[] args)
    {
        new MyClass();
    }
}
// javac MyClass.java
// java MyClass 2> Logs.txt

Exception in thread "main" java.lang.StackOverflowError

مثال C:

int main()
{
   int large[10000000] = {0};
   return 0;
}

Linux: Segmentation fault.

Windows: Unhandled exception in develop.exe: 0xC00000FD: Stackoverflow.

 

میبینید که در همه خطاها واژه stack و overflow مشترکه; 

نکته ویندوزی: توسط برنامه editbin موجود در VS و nasm32 قادرید سایز stack برنامه ها رو تغییر بدید.

توجه: خطای buffer overflow با خطای stack overflow ارتباطی نداره و این دو با هم تفاوت دارن.

 

  • حافظه Heap

حافظه Heap در قست user-space حافظه مجازی قرار داره و بصورت دستی توسط برنامه نویس مدیریت میشه. درواقع دادها در این حافظه توسط برنامه نویس ذخیره میشن و توسط خود برنامه نویس هم باید تخلیه/نابود بشن. داده های ذخیره شده در حافظه Heap، با اتمام فراخونی از بین نمیرن و تا زمانی هم که خود برنامه نویس این داده ها رو از داخل حافظه Heap پاک نکنه باقی می مونن و حتی باعث وقوع memory leak یا OutOfMemory میشن. مگر اینکه توسط Garbage Collector شناسایی و نابود بشن.

نکته: منظور از داده ها، اشیایی که بوسیله new ساخته میشن، متغیرهایی که توسط calloc/alloc/malloc تعریف میشن و مقادیری که توسط اشاره گرها جابجا میشن هستش.

نکته 2: نام دیگر این از نوع تخصیص حافظه، Dynamic memory allocation یا اختصاص حافظه بصورت پویا هستش.

 

 

سایز حافظه Heap، نسبت به سایز کل داده ها (که در بالا اشاره شد) تعیین میشه و سیستم عامل تا جایی که براش مقدور باشه و کاربر محدود نشده باشه و فضای خالی هم در RAM داشته باشه به برنامه فضای Heap میده.

نکته: در زبانهای اسکریپتی، مدیریت حافظه Heap توسط مفسر انجام میشه و در VM ها توسط runtime language.

 

  • قوائد حافظه Heap
  1. در حافظه مجازی دستگاه، قسمت Heap ذخیره میشه.
  2. محل ذخیره اشیا و داده های alloc شده هستش.
  3. حافظه Heap در مقابل مشکل memory leak و OutOfMemory پاسخگو هستش.
  4. از نظر سرعت در اشغال فضا، از حافظه Stack کندتره چون توسط pointer ها مدیریت میشه.
  5. در C++، داده ذخیره شده در حافظه Heap توسط اشاره گر فراخونده میشه و می تونه مجدداً اشغال بشه.
  6. هنگام allocation/رزرو نابخردانه حافظه می تونه OutOfMemory رخ بده.
  7. داده ها در این فضا بصورت random در کنار هم قرار می گیرن.
  8. حوزه ی حافظه Heap وابسته به runtime برنامست. یعنی با شروع برنامه اشغال و با بستن برنامه تخلیه میشه.
  9. سایز حافظه Heap برنامه هنگام runtime/شروع برنامه رزرو/allocate میشه.
  10. (در بیشتر زبانها) همه thread ها فقط از یک حافظه Heap بهره می برن.
  11. با افزایش مصرف heap، حافظه کمتری برای stack باقی می مونه.
  12. با بسته شدن برنامه، فضای allocate شده در Heap توسط برنامه هم تخلیه میشه.
  13. و...

نکته: مشکل OutOfMemory از دسته Error ها هستش و نه Exception ها.

مثال PHP:

$objTest = New MyObject(); // MyObjectIn the Heap memory.

مثال Java:

MyObject objTest = New MyObject(); // In the Heap memory.

مثال C:

int *ptr = new int; // 4 bytes in the Heap memory.

در مثال اول و دوم با ساختن شی MyObject مقداری از حافظه Heap رو اشغال کردیم، درواقع یعنی شی MyObject رو داخل حافظه Heap کردیم و نه متغیرش رو. در مثال سوم هم 4 بایت با متغیر ptr توسط malloc رو داخل حافظه Heap کردیم که بعداً باید با کدنویسی آزاد و نابودش کنیم. ولی اینکار فقط در Cpp/C و زبانهای سطح پایین امکان پذیره، و نه در جاوا, PHP و زبانهای سطح بالا!

پس تکلیف تخلیه حافظه چی میشه؟ چون حافظه هم یک حداکثر ظرفیتی رو داره و فقط بخشی کوچیکی از اون در اختیار برنامه ماست...

جواب این مسئله، Garbage Collector هستش! سیستم garbage collection برای زبانهای سطح بالا طراحی شده تا این کمبود رو تا حدودی پوشش بده.

نکته ویندوزی: توسط برنامه editbin موجود در VS و nasm32 قادرید سایز heap برنامه ها رو تغییر بدید.

 

  • Garbage Collector / GC

garbage collector (به اختصار GC) سیستمی هست که [معمولاً] بصورت خودکار اجرا میشه و شروع به چک کردن اشیایی می کنه که برنامه دیگه ازشون استفاده ای نمی کنه(dead هستن) و هیچ reference ای هم بهشون اشاره نمی کنه(unreferenced) سپس اونها رو نابود می کنه.

این همون پروسه 2 فازست که هر garbage collector ای برای تخلیه حافظه انجام میده.

معمولاً، garbage collector وقتی وارد عمل میشه که:

  1. عمل read/write در حافظه سیستم بکندی صورت میگیره.
  2. برنامه/اسکریپت کارش تموم شده و می خواد بسته بشه.
  3. بصورت دستی صدا زه بشه (مثل IDE های Netbeans, Eclipse زبان Java, PHP5.3 و...).

نکته: collect شدن یا نشدن اشیا توسط garbage collector هیچ ارتباطی به مسئله حوزه/scope نداره.

 

همونطور که گفته شد، VM ها و مفسرها اشیاه ساخته شده توسط برنامه درحال اجرا رو در حافظه Heap ذخیره میکنن. مثل اشیایی که توسط new ساخته میشن. در زمانی که Heap بکندی عمل می کنه، VM یا مفسر، سیستم garbage collection رو براه میندازه; سپس پروسه garbage collection بصورت چرخشی اجرا میشه و اشیا و داده های قابل collect شدن رو از حافظه Heap پاک می کنه. که البته کمی هم باعث کندی سیستم میشه تا عملیاتش به پایان برسه.

 

  • Memory leak

به اختصار یعنی: اشیا دیگر توسط برنامه استفاده نمی شوند، در جایی به اونها رفرنس شده و Garbage Collector هم قادر به آزاد سازی(نابود کردن) شون نیست.

یا یعنی: مشغول(in use) نگه داشتن بی دلیل اشیا ساخته شده، که برابره با نادیده گرفته شدن توسط garbage collector. (طبق قانون GC)

 

 

نکته:  memory leak ها جزو باگهای دسته critical و app-vulnerabilities هستش که از کدنویسی غلط برنامه نویس نشات می گیره.

تعاریف دیگر:

نکته 2: garbage collector بیمه ای برای رفع این گونه خرابکاری ها و کدنویسی های غلط نیست، بلکه فقط یک سیستم کمکی هستش.

 

مثال memory leak در PHP:

class MyClass
{
    // ...
}

$Obj = new MyClass;
$Obj->self = $Obj; // Memory leak by $obj.

مثال memory leak در C:

int main()
{
   char *myVar = new char[5]; // Got 5 bytes in Heap memory.
   return 0; // Memory leak by myVar, FORGOT to delete/free myVar.
}

مثال memory leak در Java ی اندروید:

@Override
protected void onCreate(Bundle state)
{
    super.onCreate(state);
    TextView myTextView = new TextView(this); // Memory leak.
    backgroundImage = getDrawable(R.drawable.large_bitmap);
    myTextView.setBackgroundDrawable(backgroundImage);
    setContentView(myTextView); // Memory leak.
}

در این مثال جاوا، 2 بار memory leak رخ میده، 1- در وضعیت کنونی بخاطر مشغول نگه داشتن متغیر شی myTextView توسط اکتیویتی 2- در وضعیتی که Orientation دستگاه تغییر کنه، بنابراین متد onCreate مجدداً فراخوانی میشه.

البته بیشترین حساسیت memory leak در سیستمهایی هستش که با محدودیت منابع روبرو هستید یا برنامه های نوع service.

 

شرح کاملی از stack, heap, data هر پروسس در حافظه مجازی

heap-stack-bss-allocation-memory

 

پیشنهاد می کنم برای درک بهتری از حافظه Heap و Stack این مقاله رو دوبار مطالعه کنید.

نظرات (۲)

۲۹ شهریور ۹۵ ، ۱۷:۱۰ علی حاجی خالویی
خیلی خوب بود

میشه لطفا ی ایمیل به من بزنید چند تا سوال ازتون بپرسم.
عالی بود لطفا بیشتر بنویسید.
کاربران بیان میتوانند بدون نیاز به تأیید، نظرات خود را ارسال کنند.
اگر قبلا در بیان ثبت نام کرده اید لطفا ابتدا وارد شوید، در غیر این صورت می توانید ثبت نام کنید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">