رزرو مشاوره تلفنی تک جلسه‌ای با استاد رضوی
رزرو مشاوره تلفنی
کنکور کامپیوتر
0
اشتراک
 

درخت هیپ (heap) چیست؟ آموزش هیپ در ساختمان داده

این صفحه عالی به آموزش هیپ (heap) در ساختمان داده پرداخته و اعمالی که در هرم قابل انجام است و مزایا و معایب حافظه Heap و مثال کدنویسی از Heap را آورده

یکی از حافظه‌هایی که در دنیای کامپیوتر و برنامه نویسی مورد استفاده قرار می‌گیرد، حافظه هیپ یا Heap است. حافظه هیپ یک حافظه پویا یا داینامیک (Dynamic) است که در واقع به جای حافظه پشته یا استک (Stack)، مورد استفاده قرار می‌گیرد. متغیر های محلی (Local Variables) معمولاً به صورت خودکار با فراخوانی تابع تخصیص داده می‌شوند و با پایان یافتن کار تابع از بین می‌روند اما در حافظه هیپ قضیه کاملاً متفاوت است. برای تخصیص حافظه هیپ، برنامه نویسان صراحتاً باید درخواست تخصیص حافظه دهند؛ به عنوان مثال در زبان‌هایی مانند جاوا یا سی پلاس پلاس، این کار به وسیله کلمه new انجام می‌شود.

به قسمت‌های حافظه هیپ که به شئ‌ها یا آبجکت‌ها تخصیص پیدا می‌کند، به اصطلاح بلاک (Block) گفته می‌شود. اندازه بلاک‌ها متفاوت است و معمولاً هنگام تخصیص حافظه به آبجکت‌ها، به صورت خودکار مشخص می‌شوند. بلاک‌های حافظه هیپ که به آبجکت‌ها تخصیص داده شده‌اند، به صورت دائمی (بر خلاف پشته) وجود دارند مگر این که اتفاقی برای آنها بیفتد و حذف شوند که به آن Deallocation نیز می‌گویند. در بعضی از زبان‌ها (مانند زبان C و زبان C++‎) یک آبجکت که درون حافظه هیپ قرار دارد، تنها زمانی از بین می‌رود که برنامه نویس به صراحت درخواست حذف یا همان عدم تخصیص آن را بدهد؛ بنابراین برنامه نویس کنترل بیشتری برروی حافظه دارد اما مسئولیتش نیز بیشتر است؛ چرا که باید با دقت تمام حافظه را مدیریت کند. اگر عدم تخصیص با دقت صورت نگیرد، هرچه بیشتر از برنامه استفاده شود، آبجکت‌های بیشتری ایجاد می‌شود بدون این که از بین بروند و در نتیجه مشکلی به وجود می‌آید با نام نشت حافظه یا Memory Leak که بسیاری از برنامه‌های ساخته شده با سی پلاس پلاس دارای این مشکل هستند.

حافظه هیپ چیست؟

در بعضی از زبان های برنامه نویسی، مدیریت حافظه هیپ به طور خودکار انجام می‌شود؛ به عنوان مثال، در زبان برنامه نویسی سی شارپ یا زبان جاوا، سیستمی با نام زباله روب یا Garbage Collection وجود دارد که عملیات مربوط به تخصیص و عدم تخصیص حافظه هیپ به آبجکت‌ها را به صورت خودکار انجام می‌دهد. در این صورت، دیگر نگرانی ما بابت مشکلات حافظه‌ای تا حدود بسیار زیادی کم می‌شود، چرا که بیشتر تمرکزمان برروی کدنویسی است و Garbage Collection به صورت خودکار عملیات آزادسازی حافظه را انجام می‌دهد. لازم است گفته شود که در زبان Java این عملیات به وسیله‌ی JVM انجام می‌شود و در زبان #C به وسیله‌ی CLR

حذف آبجکت ها از حافظه هیپ

موارد مختلفی وجود دارد که باعث پاک شدن یا همان عدم تخصیص آبجکت از درون حافظه هیپ می‌شود. در اینجا به 3 مورد از متداول‌ترین علت‌های حذف آبجکت از درون هیپ را آورده‌ایم:

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

تخصیص حافظه در هیپ

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

تخصیص حافظه در هیپ

هر تخصیص، به صورت پیوسته درون حافظه هیپ به صورت بلاک قرار می‌گیرد و ارجاعی به آنها در متغیرهای محلی نیز ایجاد می‌شود. در اینجا Heap Manager می‌تواند تخصیص حافظه را در هر بلاکی که می‌خواهد ایجاد کند؛ تنها نباید با بقیه بلاک‌ها تداخل یا Overlap داشته باشد؛ به عنوان مثال، تخصیص حافظه در تصویر بالا به صورت از پایین به بالا بود که البته اجباری بر این حالت (در حافظه هیپ) نیست. در هر زمان از اجرای برنامه، بلاک‌هایی درون حافظه هیپ توسط متغیرهای محلی مورد ارجاع واقع شده‌اند که به آنها بلاک‌های در حال استفاده یا In Use می‌گویند. فضاهای دیگری نیز وجود دارد که با نام فضای خالی یا Free شناخته می‌شود که آماده تخصیص داده شدن به آبجکت‌های جدید هستند. در تصویر بالا حافظه خالی با مستطیل سبز رنگ مشخص شده است.

گرفتن حافظه یا Deallocation در هیپ

وقتی قسمتی از برنامه که بعضی از بلاک‌های حافظه منحصراً به آن اختصاص داده شده بود به پایان رسید، در بعضی از زبان‌ها به صراحت باید آن آبجکت‌ها را حذف یا Deallocate کرد. در زبان‌هایی مانند جاوا، این فضاها که ارجاعی به آنها وجود ندارد توسط Garbage Collection پاک یا Clean می‌شوند و آماده تخصیص به آبجکت‌های دیگر هستند. Garbage Collection به صورت ضمنی فضای حافظه Heap را خالی از بلاک‌های بلااستفاده می‌کند. در تصویر زیر مشاهده می‌کنید که با حذف متغیر محلی دوم، بلوک مربوط به تصویر PNG دوم نیز به حالت Free درآمده است:

عدم تخصیص حافظه در هیپ

مزایا و معایب حافظه Heap

حافظه هیپ دارای یک‌سری مزایا و معایب (نسبت به پشته) دارد که در این قسمت به مهم‌ترین آنها می‌پردازیم:

مزایای حافظه هیپ

  • طول عمر: با توجه به این که تخصیص حافظه تماماً توسط برنامه نویس صورت می‌پذیرد، در نتیجه امکان ایجاد یک ساختمان داده منحصربه‌فرد درون حافظه وجود دارد و این ساختمان داده را می‌توان به فراخوان کننده توابع (Caller) برگرداند؛ همچین چیزی در متغیرهای محلی و حافظه Stack وجود ندارد، چرا که با اتمام کار تابع، تخصیص آن متغیر نیز از بین خواهد رفت.
  • اندازه: اندازه حافظه تخصیص داده شده می‌تواند با جزئیات بیشتری کنترل شود؛ به عنوان مثال، اندازه یک بافر رشته‌‍ای یا String Buffer می‌تواند در حین اجرا (Run-Time) مشخص شود. برخلاف متغیرهای محلی که یک مقدار ثابت تعیین می‌کنند و امیدوارند که اندازه مناسبی باشد.

معایب حافظه هیپ

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

مقایسه حافظه هیپ و پشته

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

اهداف

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

تخصیص و عدم تخصیص

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

طول عمر

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

دسترسی به داده ها

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

قطعه قطعه شدن (Fragmentation)

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

در جدول زیر، مقایسه بین پشته و هیپ آمده است:

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

یک مثال کدنویسی از Heap

در کد زیر که یک مثال از حافظه Heap در جاوا است، کلاسی با نام Person ایجاد شده که دارای دو متغیر name و age است؛ سپس در کلاس HeapExample یک آبجکت از این متغیر به همراه یک آرایه ایجاد شده است که هردو درون حافظه هیپ قرار می‌گیرند. جزئیات مربوط به هر خط کد نیز به صورت کامنت (Comment) بالای خطوط نوشته شده است:

class Person {
    private String name;
    private int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class HeapExample {
    public static void main(String[] args) {
        // تخصیص یک آرایه با اندازه 5 در حافظه هیپ
        int[] numbers = new int[5];
 
        for (int i = 0; i < 5; i++) {
            numbers[i] = i + 1; // ذخیره مقادیر 1 تا 5 درون خانه های آرایه   
        }

        // ایجاد یک آبجکت درون حافظه هیپ
        Person person = new Person("John", 30);

        // تغییر متغیر مربوط به آبجکت سن   
        person.setAge(31);

        // دسترسی به مقادیر ذخیره شده در آرایه واقع در هیپ
        for (int i = 0; i < 5; i++) {
            System.out.println("Number at index " + i + ": " + numbers[i]);
        }

        // دسترسی به مشخصه های آبجکت ایجاد شده از داخل هیپ
        System.out.println("Person: " + person.getName() + ", Age: " + person.getAge());
    }
}

جمع‌بندی

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

حافظه هیپ (Heap) چیست؟

حافظه هیپ یک حافظه پویا یا داینامیک (Dynamic) است که در واقع به جای حافظه پشته یا استک (Stack) برای ذخیره داده‌های برنامه نویسی مورد استفاده قرار می‌گیرد.

مدیریت حافظه هیپ بر عهده کیست؟

بستگی دارد، در زبان‌هایی مانند C و ++C، برنامه نویس به طور مستقیم به مدیریت حافظه هیپ می‌پردازد؛ اما در زبان‌هایی مانند C# ،Java و Python، سیستمی با نام Garbage Collection به طور خودکار این حافظه را مدیریت می‌کند.

چه نوع داده هایی در پشته ذخیره می‌شوند؟

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

امتیازدهی51111111111امتیاز 5.00(4 امتیاز)
اشتراک
تلگرام اینستاگرام تماس با پشتیبانی: 09378555200 تماس با پشتیبانی: 09378555200
خدمات ما پشتیبانی کنکور کامپیوتر

سؤالی داری؟ از هر راهی که راحت‌تری.

تماس با پشتیبانی۰۹۳۷۸۵۵۵۲۰۰ پشتیبانی در تلگرامپاسخ‌گویی پیامی کانال تلگراماخبار و اطلاعیه‌ها اینستاگرامkonkurcomputer.ir
اشتراک‌گذاری این مطلب

درخت هیپ (heap) چیست؟ آموزش هیپ در ساختمان داده

تلگرام واتس‌اپ ایکس
https://www.konkurcomputer.ir/heap-data-structure.html
خدمات کنکور کامپیوتر
هر چیزی که برای قبولی لازم داری، یک‌جاخدمات ما

از مشاوره‌ی شخصی با استاد رضوی تا فیلم‌های دروس، پلتفرم‌ها و ابزارهای رایگان

مشاوره‌ی شخصی با استاد رضوی
تماس با پشتیبانی