۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

جمع آوری زباله پایتون و ماژول gc

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

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

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

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

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

چگونه پایتون حافظه را مدیریت می کند

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

مرجع چیست؟ هر چیزی که امکان دسترسی به یک شی را از طریق یک نام یا از طریق یک دسترسی در یک شی دیگر فراهم می کند.

یک مثال ساده در اینجا آمده است:


x = "Hello there"

وقتی این دستور را به پایتون می‌دهیم، دو چیز در زیر هود اتفاق می‌افتد:

  1. رشته "Hello there" ایجاد شده و به عنوان یک شی پایتون در حافظه ذخیره می شود.
  2. نام x در فضای نام محلی ایجاد می‌شود و به آن شی اشاره می‌کند که تعداد مراجع آن را ۱ به ۱ افزایش می‌دهد.

اگر بخواهیم y = x را بگوییم، تعداد مراجع یک بار دیگر به ۲ افزایش می یابد.

هرگاه x و y از محدوده نام خود خارج شوند یا از فضای نام خود حذف شوند، تعداد مرجع رشته برای هر یک از آن نام ها ۱ کاهش می یابد. هنگامی که x و y هر دو خارج از محدوده یا حذف شدند، تعداد مجدد رشته به ۰ می‌رود و حذف می‌شود.

“scope” چیست؟

Scope اصطلاح کلی برای در دسترس بودن یک نام خاص است. به عنوان مثال، محدوده یک متغیر تعریف شده در داخل یک تابع به طور پیش فرض فقط آن تابع است، اما محدوده نام تعریف شده در سطح ماژول کل ماژول است. برای جزئیات بیشتر، مستندات پایتون را ببینید.< /p>

حالا، فرض کنید یک لیست با یک رشته در آن ایجاد می کنیم، مانند این:


x = ["Hello there", 2, False]

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

گزارش نشان می دهد که تعداد کمی از پروژه های منبع باز به طور فعال نگهداری می شوند

اکنون این مثال را در نظر بگیرید:


x = "Hello there"
y = [x]

اگر اولین عنصر را از y حذف کنیم، یا فهرست y را به طور کامل حذف کنیم، رشته هنوز در حافظه است. این به این دلیل است که نام x به آن اشاره دارد.

چرخه های مرجع در پایتون

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

در اینجا یک مثال ساختگی آمده است:


x = SomeClass()
y = SomeOtherClass()
x.item = y
y.item = x

از آنجایی که x و y به یکدیگر ارجاع دارند، هرگز از سیستم حذف نخواهند شد – حتی اگر هیچ مرجع دیگری به هیچ یک از آنها نباشد.

در واقع برای زمان اجرا خود پایتون ایجاد چرخه های مرجع برای اشیا بسیار رایج است. یک مثال می تواند یک استثنا با یک شی ردیابی باشد که حاوی ارجاعاتی به خود استثنا است.

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

مجموعه زباله پایتون (gc)

زباله گرد پایتون اشیاء را با چرخه های مرجع تشخیص می دهد. این کار را با ردیابی اشیایی که “کانتینر” هستند – چیزهایی مانند لیست ها، فرهنگ لغت ها، نمونه های کلاس سفارشی – و تعیین اینکه چه اشیایی در آنها در جای دیگری قابل دسترسی نیستند، انجام می دهد.

هنگامی که آن اشیاء مشخص شدند، جمع‌آورنده زباله آنها را با اطمینان از اینکه می‌توان تعداد ارجاعات آنها را به صفر رساند، حذف می‌کند. (برای اطلاعات بیشتر درباره نحوه کار، راهنمای توسعه دهنده Python را ببینید.)

اکثریت قریب به اتفاق اشیاء پایتون چرخه مرجع ندارند، بنابراین جمع‌آورنده زباله نیازی به کار ۲۴/۷ ندارد. در عوض، جمع‌آورنده زباله از چند اکتشافی استفاده می‌کند تا کمتر اجرا شود و هر بار تا حد امکان کارآمد باشد.

هنگامی که مفسر پایتون شروع به کار می‌کند، تعداد شی‌هایی را که تخصیص داده شده است، اما تخصیص داده نشده است، ردیابی می‌کند. اکثریت قریب به اتفاق اشیاء پایتون طول عمر بسیار کوتاهی دارند، بنابراین به سرعت وارد و خارج می شوند. اما با گذشت زمان، اشیاء با عمر طولانی بیشتری در اطراف آویزان می شوند. هنگامی که بیش از تعداد معینی از این اشیاء روی هم قرار می گیرند، زباله جمع کن کار می کند. (تعداد پیش فرض اشیاء با عمر طولانی مجاز ۷۰۰ تا از Python 3.10 است.)

هر بار که زباله جمع کن اجرا می شود، تمام اشیایی را که از مجموعه باقی مانده اند را می گیرد و آنها را در گروهی به نام نسل کنار هم قرار می دهد. این اشیاء “نسل ۱” کمتر برای چرخه های مرجع اسکن می شوند. هر شیء نسل ۱ که از جمع‌کننده زباله زنده بماند، در نهایت به نسل دوم منتقل می‌شود، جایی که به ندرت اسکن می‌شوند.

ردیابی خطا با Sentry، Python و Django

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

نحوه استفاده از ماژول gc

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

یک کاری مفید که gc به شما امکان می دهد انجام دهید این است که وقتی مطمئن شدید به آن نیاز ندارید، زباله جمع کن را خاموش کنید. به عنوان مثال، اگر یک اسکریپت کوتاه مدت دارید که اشیاء زیادی را روی هم انباشته می کند، به جمع کننده زباله نیازی ندارید. وقتی اسکریپت تمام شود همه چیز پاک می شود. برای این منظور، می‌توانید جمع‌آورنده زباله را با دستور gc.disable() غیرفعال کنید. بعداً می‌توانید آن را با gc.enable() دوباره فعال کنید.

همچنین می توانید یک چرخه مجموعه را به صورت دستی با gc.collect() اجرا کنید. یک برنامه رایج برای این کار، مدیریت بخش پرفورمنس برنامه شما است که بسیاری از اشیاء موقت را تولید می کند. می‌توانید جمع‌آوری زباله را در طول آن بخش از برنامه غیرفعال کنید، سپس به صورت دستی یک مجموعه را در پایان اجرا کنید و مجموعه را دوباره فعال کنید.

یکی دیگر از بهینه سازی مفید جمع آوری زباله gc.freeze() است. هنگامی که این دستور صادر می شود، همه چیزهایی که در حال حاضر توسط جمع کننده زباله ردیابی می شوند، “یخ زده” می شوند، یا به عنوان معاف از اسکن های جمع آوری آینده فهرست می شوند. به این ترتیب، اسکن‌های آینده می‌توانند از روی آن اشیا عبور کنند. اگر برنامه‌ای دارید که کتابخانه‌ها را وارد می‌کند و وضعیت داخلی خوبی را قبل از شروع راه‌اندازی می‌کند، می‌توانید پس از انجام تمام کار gc.freeze() را صادر کنید. این کار باعث می شود زباله جمع کن مجبور نباشد روی چیزهایی که به هر حال به احتمال زیاد حذف نمی شوند، ترال کند. (اگر می خواهید دوباره جمع آوری زباله روی اشیاء یخ زده انجام شود، از gc.unfreeze() استفاده کنید.)

اشکال‌زدایی مجموعه زباله با gc

همچنین می‌توانید از gc برای اشکال‌زدایی رفتارهای جمع‌آوری زباله استفاده کنید. اگر تعداد نامحدودی از اشیاء دارید که در حافظه انباشته شده اند و زباله جمع آوری نمی شوند، می توانید از ابزارهای بازرسی gc استفاده کنید تا بفهمید چه چیزی ممکن است ارجاعات به آن اشیاء را نگه دارد.

اگر می خواهید بدانید چه اشیایی به یک شی معین اشاره دارند، می توانید از gc.get_referrers(obj) برای فهرست کردن آنها استفاده کنید. همچنین می توانید از gc.get_referents(obj) برای یافتن هر شیئی که توسط یک شی معین ارجاع داده شده است استفاده کنید.

نحوه ایجاد اسناد Word از R یا Python با Quarto

اگر مطمئن نیستید که یک شی معین کاندیدای جمع‌آوری زباله است، gc.is_tracked(obj) به شما می‌گوید که آیا آن شی توسط جمع‌آورنده زباله ردیابی می‌شود یا خیر. همانطور که قبلاً ذکر شد، به خاطر داشته باشید که جمع‌آورنده زباله اشیاء “اتمی” (مانند اعداد صحیح) یا عناصری که فقط شامل اشیاء اتمی هستند را ردیابی نمی‌کند.

اگر می‌خواهید خودتان ببینید چه اشیایی جمع‌آوری می‌شوند، می‌توانید پرچم‌های رفع اشکال جمع‌آورنده زباله را با gc.set_debug(gc.DEBUG_LEAK|gc.DEBUG_STATS) تنظیم کنید. این اطلاعات مربوط به جمع آوری زباله را در stderr می نویسد. تمام اشیاء جمع آوری شده به عنوان زباله در لیست فقط خواندنی gc.garbage را حفظ می کند.

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

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

اجازه دهید با چند اشاره گر برای جلوگیری از جمع آوری اشیا به پایان برسانیم.

به محدوده شیء توجه کنید

اگر شی ۱ را به عنوان یکی از ویژگی های شی ۲ (مانند یک کلاس) اختصاص دهید، قبل از اینکه شی ۱ این کار را انجام دهد، شی ۲ باید از محدوده خارج شود:


obj1 = MyClass()
obj2.prop = obj1

علاوه بر این، اگر این اتفاق به‌گونه‌ای رخ دهد که یک اثر جانبی عملیات دیگر باشد، مانند ارسال Object 2 به عنوان آرگومان به سازنده برای Object 1، ممکن است متوجه نشوید که Object 1 دارای یک مرجع است:


obj1 = MyClass(obj2)

یک مثال دیگر: اگر یک شی را به یک لیست در سطح ماژول فشار دهید و لیست را فراموش کنید، آن شی باقی می ماند تا زمانی که از لیست حذف نشود یا تا زمانی که خود لیست دیگر هیچ مرجعی نداشته باشد. اما اگر آن لیست یک شی در سطح ماژول باشد، احتمالاً تا زمانی که برنامه خاتمه یابد، باقی خواهد ماند.

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

برای اجتناب از چرخه های مرجع از ضعیف رفر استفاده کنید

ماژول ضعیف پایتون به شما امکان می دهد مرجعات ضعیف به اشیاء دیگر. ارجاعات ضعیف تعداد مراجع یک شی را افزایش نمی دهند، بنابراین شیئی که فقط مراجع ضعیف دارد، کاندیدای جمع آوری زباله است.

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

چرخه های مرجع را به صورت دستی شکسته

در نهایت، اگر می‌دانید که یک شی معین ارجاع به شی دیگری را دارد، همیشه می‌توانید ارجاع به آن شی را به صورت دستی بشکنید. برای مثال، اگر instance_of_class.ref = other_object دارید، می‌توانید هنگام آماده‌سازی برای حذف instance_of_class، instance_of_class.ref = None را تنظیم کنید.