یوشا آل ایوب

وبگاه دست نوشته ها و تجربیات شخصی
بِسمِ اللّه‏ِ الرَّحمنِ الرَّحيمِ

یوشا آل ایوب

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

یوشا آل ایوب

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

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

حافظه Heap و Stack

۹۴/۰۵/۲۳

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

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

 

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

  • حافظه Stack
  • حافظه Heap
  • Memory leak
  • نکاتی درباب اندروید

 

  • حافظه Stack

Stack درواقع مثل دسته ای از بشقاب هستش که روی هم قرار گرفتن... هروقت بخواهید بشقابی رو روی این دسته قرار بدید معمولاً اون رو در بالا قرار میدید. به همین ترتیب هروقت بخواهید بشقابی رو از این دسته بردارید همیشه بشقاب بالای رو برمیدارید... که این دسته رو Stack هه LILO یا همون LastIn-FirstOut می نامن.

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

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

مثل:

int myVariable = 10; // To Stack memory.
یا
function myFunction()
{
   $_testVar = 1; // To Stack memory.
   return; // To Stack memory.
}

قوائد حافظه Stack

  • در حافظه مجازی دستگاه، قسمت Stack ذخیره میشه.
  • محل ذخیره متغیرها، پارامتر های توابع، آدرس های بازگشتی توابع هست. (همچنین ردگیری توابع تودرتو)
  • از نظر سرعت در اشغال فضا، از حافظه Heap سریعتره.
  • در زبان C/C++، کلاس ها و ساختمان ها درصورت عدم استفاده از اشاره گر در حافظه Stack ذخیره میشن.
  • در حین allocation/اشغال نابخردانه می تونه stack-overflow رخ بده.
  • سایز Stack برنامه در زمان کامپایل set میشه.
  • هر برنامه یک thread main داره و هر thread یک حافظه Stack خصوصی داره. پس تا وقتی که thread بسته نشده Stack ش هم وجود داره. (دراینجا بستن = exit و نه terminate)
  • حوزه ی حافظه Stack برنامه به thread اون برنامه ضمیمه شده.
  • item ها بترتیب بر روی هم قرار می گیرند.
  • و...

حالا با اجرای کدهای زیر درک بیشتری نسبت به حافظه Stack و Stack-Overflow پیدا می کنید!

مثال PHP:

function myInfiniteRecursion()
{
    myInfiniteRecursion();
    return;
}

myInfiniteRecursion();

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

 

مثال PHP #2:

for ($i = 1e5; $i --;) $test = (object)[$test];

 

مثال Java:

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

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

Exception in thread "main" java.lang.StackOverflowError

مثال C:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    // Method 1
    // int bang[10000000] = {0};

    // Method 2
    char inputBuffer[5];
    strcpy(inputBuffer, argv[1]);
    return 0;
}
// #gcc test.c -o test
// #test 123456

Linux: Segmentation fault.

Windows: Unhandled exception in Test.exe: 0xC00000FD: Stack Overflow.

 

میبینید که در همه خطاها واژه stack و overflow مشترکه; که تنها به دو دلیل رخ دادن: تابع تودرتوی بینهایت یا متغیر حجیم!

 

  • حافظه Heap

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

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

 

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

 

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

قوائد حافظه Heap

  • در حافظه مجازی دستگاه، قسمت Heap ذخیره میشه.
  • محل ذخیره اشیا و داده های alloc شده هستش.
  • از نظر سرعت در اشغال فضا، از حافظه Stack کندتره.
  • در C++، داده ذخیره شده در حافظه Heap توسط اشاره گر فراخونده میشه و می تونه مجدداً اشغال بشه.
  • در حین allocation/اشغال نابخردانه می تونه OutOfMemory رخ بده.
  • item ها بصورت random در کنار هم قرار می گیرن.
  • حوزه ی حافظه Heap برنامه وابسته به runtime برنامست. یعنی با شروع برنامه اشغال و با بستن برنامه تخلیه میشه.
  • سایز حافظه Heap برنامه زمانی set میشه که برنامه شروع بشه.
  • (در بیشتر زبانها) همه thread ها فقط از یک حافظه Heap بهره می برن.
  • حافظه Heap در مقابل memory leak پاسخگو هستش.
  • و...

مثل:

MyObject objTest = New MyObject(); // To Heap memory.

یا مثل:

int* myVariable = (int*) malloc(10 * sizeof(int)); // To Heap memory.

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

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

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

 

Garbage collector / GC

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

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

 

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

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

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

 

همونطور که گفته شد، مثل VM ها و مفسرها اشیاه ساخته شده توسط برنامه درحال اجرا رو در حافظه Heap ذخیره میکنن. مثل اشیایی که توسط new ساخته میشن. در زمانی که Heap بکندی عمل می کنه، VM یا مفسر، سیستم garbage collection رو براه میندازه; سپس پروسه garbage collector بصورت چرخشی اجرا میشه و اشیا و داده های قابل 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.
    return;
}

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

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

 

نکاتی درباب اندروید

حداکثر ظرفیت حافظه Heap سیستم عامل اندروید، بیشتر مبتنی بر اندازه resolution و مقدار density صفحه هست.

برای نمونه، ظرفیت حافظه Heap در گوشی HTC Salsa فقط 20 مگابایته ولی در گوشی HTC Desire بین 32 تا 48 مگابایته! درصورتی که هردو از اندروید 2.3.3 استفاده می کنن، حدوداً 400 تا 600 مگابایت RAM دارن و برای یک شرکت هم هستن. البته فقط درصد کمی از این مقدار مختص برنامه ماست، بقیه صرف خود سیستم عامل، DVM، سرویس ها و غیره میشه.

 

نکته: resolution، تعداد پیکسل های افقی و عمودی در صفحه رو تعریف می کنه. ولی density فاصله اون پیکسل ها نسبت به یکدیگر رو تعریف میکنه. (که درواقع sharp بودن یا نبودن صفحه رو تعیین میکنه - کلمه اختصاریش هم DPI هستش)

 

تجاوز از حد مجاز حافظه Heap، دستگاه رو به وضعیت LowMemory میبره و سپس DVM رو مجبور میکنه تا garbage collector رو اجرا کنه که از اون وضعیت خارج بشه. و درصورت عدم موفقیت، نهایتاً برنامه ها رو با خطای OutOfMemoryError می بنده.

(البته در اندروید و Cyanogen، سایز حافظه Heap برنامه بصورت دستی هم می تونه تنظیم بشه)

 

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

نظرات (۲)

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

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