خانه / آموزش‌ها / مدیریت حافظه و Garbage Collection در پایتون

مدیریت حافظه و Garbage Collection در پایتون

🐍 HomeOfPython
|
📅 1404/10/22

سطح مقدماتی (Beginner Level)

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

در زبان‌هایی مانند C یا C++، برنامه‌نویس باید حافظه را به صورت دستی رزرو (Allocate) و آزاد (Deallocate) کند. اما در پایتون، Garbage Collector (زباله‌روب) و سیستم مدیریت حافظه خودکار این وظیفه را بر عهده دارند.

متغیرها: جعبه یا برچسب؟

در بسیاری از زبان‌ها، متغیرها مانند "جعبه‌هایی" هستند که مقادیر را در خود نگه می‌دارند. اما در پایتون، متغیرها شبیه به برچسب‌ها (Labels) یا Referenceهایی هستند که به یک شیء (Object) در حافظه اشاره می‌کنند.

وقتی می‌نویسید a = 10، پایتون یک شیء با مقدار 10 در حافظه ایجاد می‌کند و برچسب a را روی آن می‌چسباند.

Python

مفهوم Identity و تابع id()

هر شیء در پایتون یک شناسه منحصر‌به‌فرد (Identity) دارد که در واقع آدرس آن در حافظه است. تابع id() این آدرس را برمی‌گرداند.

Python

شمارش ارجاع (Reference Counting) - ساده شده

مکانیزم اصلی پایتون برای آزادسازی حافظه، Reference Counting است. هر شیء در پایتون یک شمارنده دارد که تعداد متغیرهایی که به آن اشاره می‌کنند را نگه می‌دارد.

  1. وقتی متغیری به شیء اختصاص یابد، شمارنده +1 می‌شود.
  2. وقتی متغیر حذف شود (با del) یا به چیز دیگری اشاره کند، شمارنده -1 می‌شود.
  3. وقتی شمارنده به 0 برسد، پایتون آن شیء را از حافظه پاک می‌کند.
python
# مثال ۳: مفهوم شمارش ارجاع (Pseudo-code)
a = [10, 20]  # Reference count = 1
b = a         # Reference count = 2
del a         # Reference count = 1
del b         # Reference count = 0 -> شیء [10, 20] پاک می‌شود

سطح پیشرفته (Professional Level)

در سطح حرفه‌ای، مدیریت حافظه پایتون فراتر از شمارش ارجاع ساده است. مسائلی مانند Circular References (ارجاعات چرخشی) و بهینه‌سازی حافظه برای میلیون‌ها شیء نیازمند دانش عمیق‌تری است.

چرخه مدیریت حافظه

مشکل ارجاعات چرخشی (Circular References)

شمارش ارجاع (Reference Counting) بسیار سریع است، اما یک نقطه ضعف بزرگ دارد: نمی‌تواند ارجاعات چرخشی را تشخیص دهد. اگر شیء A به B اشاره کند و شیء B به A، شمارنده ارجاع هرگز صفر نمی‌شود، حتی اگر هیچ متغیر خارجی به آن‌ها دسترسی نداشته باشد. اینجاست که Generational Garbage Collector وارد عمل می‌شود.

Python

ماژول gc و نسل‌های زباله‌روبی

پایتون برای حل مشکل بالا، یک Garbage Collector (GC) جداگانه دارد که هر از گاهی اجرا می‌شود تا چرخه‌ها را پیدا کند. این GC بر اساس سه نسل (Generations) کار می‌کند:

  • Generation 0: اشیاء تازه ایجاد شده. بیشترین بررسی روی این نسل انجام می‌شود.
  • Generation 1: اشیایی که از Gen 0 جان سالم به در برده‌اند.
  • Generation 2: اشیاء قدیمی و پایدار. کمترین بررسی روی این‌ها انجام می‌شود.

شما می‌توانید با ماژول gc با این فرآیند تعامل کنید.

Python

بررسی دقیق تعداد ارجاعات با sys.getrefcount

برای دیدن تعداد دقیق ارجاعات به یک شیء از ماژول sys استفاده می‌کنیم. توجه کنید که خود تابع getrefcount یک ارجاع موقت ایجاد می‌کند، بنابراین عدد خروجی معمولاً یکی بیشتر از انتظار است.

Python

بهینه‌سازی حافظه با __slots__

در کلاس‌های پایتون، ویژگی‌ها (Attributes) در یک دیکشنری (__dict__) ذخیره می‌شوند. دیکشنری‌ها حافظه زیادی مصرف می‌کنند. اگر کلاسی دارید که میلیون‌ها نمونه (Instance) از آن ساخته می‌شود، استفاده از __slots__ می‌تواند مصرف حافظه را ۴۰ تا ۵۰ درصد کاهش دهد. این دستور به پایتون می‌گوید برای ویژگی‌ها فضای ثابت در نظر بگیرد و از دیکشنری استفاده نکند.

Python

نکات کلیدی برای حرفه‌ای‌ها

  1. از __del__ پرهیز کنید: استفاده از متد مخرب __del__ می‌تواند در عملکرد Garbage Collector اختلال ایجاد کند (مخصوصاً در نسخه‌های قدیمی‌تر پایتون در مواجهه با ارجاعات چرخشی).
  2. Weak References: برای کش‌کردن (Caching) اشیاء بدون افزایش Reference Count، از ماژول weakref استفاده کنید. این کار مانع از پاک شدن شیء توسط GC نمی‌شود.
  3. Explicit GC: در برنامه‌های Real-time (مثل بازی‌ها)، ممکن است بخواهید GC اتوماتیک را غیرفعال کنید و در زمان‌های خاصی (مثلاً هنگام لودینگ) gc.collect() را دستی صدا بزنید تا از فریز شدن برنامه جلوگیری شود.