پشتیبانی از زبالهروبی چرخهای¶
پشتیبانی پایتون از تشخیص و جمعآوری زبالههایی که شامل ارجاعهای چرخهای هستند، نیازمند پشتیبانی از سوی نوعهای شیء است که «ظرف»هایی برای اشیاء دیگر به شمار میروند و ممکن است خود آن اشیاء نیز ظرف باشند. نوعهایی که ارجاعی به اشیاء دیگر ذخیره نمیکنند، یا تنها ارجاعهایی به نوعهای اتمی (مانند اعداد یا رشتهها) ذخیره میکنند، نیازی به ارائه هیچ پشتیبانی صریحی برای زبالهروبی ندارند.
برای ایجاد یک نوع ظرف، فیلد tp_flags شیء نوع باید شامل Py_TPFLAGS_HAVE_GC باشد و پیادهسازی هندلر tp_traverse ارائه شود. اگر نمونههای نوع تغییرپذیر باشند، پیادهسازی tp_clear نیز باید ارائه شود.
Py_TPFLAGS_HAVE_GCاشیایی با نوعی که این پرچم در آن تنظیم شده است باید با قواعدی که در اینجا مستند شدهاند مطابقت داشته باشند. برای سهولت، به این اشیاء «اشیاء ظرف» گفته خواهد شد.
سازندههای انواع ظرف باید از دو قاعده پیروی کنند:
حافظه برای شیء باید با استفاده از
PyObject_GC_NewیاPyObject_GC_NewVarتخصیص یابد.پس از آنکه تمامی فیلدهایی که ممکن است حاوی ارجاعهایی به ظرفهای دیگر باشند مقداردهی اولیه شدند، باید
PyObject_GC_Track()را فراخوانی کند.
بهطور مشابه، آزادساز حافظهی شیء باید از جفت قاعدهی مشابهی پیروی کند:
پیش از آنکه فیلدهایی که به ظرفهای دیگر ارجاع دارند نامعتبر شوند، باید
PyObject_GC_UnTrack()فراخوانی شود.حافظهی شیء باید با استفاده از
PyObject_GC_Del()آزاد شود.هشدار
اگر نوعی Py_TPFLAGS_HAVE_GC را اضافه کند، آنگاه باید حداقل یک هندلر
tp_traverseرا پیادهسازی کند یا صریحاً از هندلری در زیرکلاس یا زیرکلاسهای خود استفاده کند.هنگام فراخوانی
PyType_Ready()یا برخی از APIهایی که بهطور غیرمستقیم آن را فراخوانی میکنند، مانندPyType_FromSpecWithBases()یاPyType_FromSpec()، اگر نوع از کلاسی ارثبری کند که پروتکل زبالهروب را پیادهسازی کرده باشد و کلاس فرزند شامل پرچمPy_TPFLAGS_HAVE_GCنباشد، مفسر بهطور خودکار فیلدهایtp_flags،tp_traverseوtp_clearرا پر میکند.
-
PyObject_GC_New(TYPE, typeobj)¶
مشابه
PyObject_Newاست، اما برای اشیاء ظرفی است که پرچمPy_TPFLAGS_HAVE_GCروی آنها تنظیم شده است.برای تخصیص حافظه به یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض، جایگاه
tp_allocنوع را فراخوانی کنید.هنگام پر کردن جایگاه
tp_allocیک نوع،PyType_GenericAlloc()به تابع سفارشیای که صرفاً این ماکرو را فراخوانی میکند ترجیح داده میشود.حافظهی تخصیصیافته توسط این ماکرو باید با
PyObject_GC_Del()آزاد شود (که معمولاً از طریق جایگاهtp_freeشیء فراخوانی میشود).همچنین ببینید
-
PyObject_GC_NewVar(TYPE, typeobj, size)¶
مشابه
PyObject_NewVarاست، اما برای اشیاء ظرفی که پرچمPy_TPFLAGS_HAVE_GCبرایشان تنظیم شده است.برای تخصیص حافظه به یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض، جایگاه
tp_allocنوع را فراخوانی کنید.هنگام پر کردن جایگاه
tp_allocیک نوع،PyType_GenericAlloc()به تابع سفارشیای که صرفاً این ماکرو را فراخوانی میکند ترجیح داده میشود.حافظهی تخصیصیافته توسط این ماکرو باید با
PyObject_GC_Del()آزاد شود (که معمولاً از طریق جایگاهtp_freeشیء فراخوانی میشود).همچنین ببینید
-
PyObject *PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)¶
- این است API ناپایداراین ممکن است بدون هشدار در نسخههای جزئی تغییر کند.
مشابه
PyObject_GC_Newاست، اما extra_size بایت در انتهای شیء تخصیص میدهد (در آفستtp_basicsize). حافظهی تخصیصیافته با صفر مقداردهی اولیه میشود، بهجزسرآیند شیء پایتون.دادهی اضافی به همراه شیء آزادسازی خواهد شد، اما در غیر این صورت توسط پایتون مدیریت نمیشود.
حافظهی تخصیصیافته توسط این تابع باید با
PyObject_GC_Del()آزاد شود (که معمولاً از طریق جایگاهtp_freeشیء فراخوانی میشود).هشدار
این تابع بهعنوان ناپاید علامتگذاری شده است زیرا سازوکار نهایی برای رزرو دادههای اضافی پس از یک نمونه هنوز تصمیمگیری نشده است. برای تخصیص تعداد متغیری از فیلدها، بهتر است بهجای آن از
PyVarObjectوtp_itemsizeاستفاده کنید.اضافه شده در نسخهی 3.12.
-
PyObject_GC_Resize(TYPE, op, newsize)¶
تغییر اندازهی شیءای که توسط
PyObject_NewVarتخصیصیافته است. شیء تغییراندازهیافته از نوعTYPE*(منظور هر نوع C است) را برمیگرداند یا در صورت شکستNULL.op باید از نوع PyVarObject* باشد و هنوز توسط جمعکننده زباله پیگیری نشده باشد. newsize باید از نوع
Py_ssize_tباشد.
-
void PyObject_GC_Track(PyObject *op)¶
- قسمتی از ABI پایدار.
شیء op را به مجموعهی اشیاء ظرفی که توسط زبالهروب پیگیری میشوند اضافه میکند. زبالهروب میتواند در زمانهای غیرمنتظره اجرا شود، بنابراین اشیاء باید در حین پیگیری معتبر باشند. این تابع باید پس از معتبر شدن همهی فیلدهایی که هندلر
tp_traverseدنبال میکند فراخوانی شود، که معمولاً نزدیک به پایان سازنده انجام میشود.
-
int PyObject_IS_GC(PyObject *obj)¶
اگر شیء پروتکل جمعآورنده زباله را پیادهسازی کند، مقدار غیرصفر برمیگرداند؛ در غیر این صورت ۰ برمیگرداند.
اگر این تابع مقدار 0 را برگرداند، شیء توسط زبالهروب قابل پیگیری نخواهد بود.
-
int PyObject_GC_IsTracked(PyObject *op)¶
- قسمتی از ABI پایدار از نسخهی 3.9.
اگر نوع شیء op پروتکل GC را پیادهسازی کرده باشد و op در حال حاضر توسط زبالهروب پیگیری شود، ۱ را برمیگرداند و در غیر این صورت ۰.
این مشابه تابع پایتون
gc.is_tracked()است.اضافه شده در نسخهی 3.9.
-
int PyObject_GC_IsFinalized(PyObject *op)¶
- قسمتی از ABI پایدار از نسخهی 3.9.
اگر نوع شیء op پروتکل زبالهروبی را پیادهسازی کرده باشد و op قبلاً توسط زبالهروب نهاییسازی شده باشد، مقدار ۱ و در غیر این صورت مقدار ۰ را برمیگرداند.
این مشابه تابع پایتونی
gc.is_finalized()است.اضافه شده در نسخهی 3.9.
-
void PyObject_GC_Del(void *op)¶
- قسمتی از ABI پایدار.
حافظهای را که با
PyObject_GC_NewیاPyObject_GC_NewVarبه یک شیء تخصیص یافته است، آزاد میکند.برای آزاد کردن حافظهی یک شیء، این را مستقیماً فراخوانی نکنید؛ در عوض جایگاه
tp_freeنوع را فراخوانی کنید.از این برای حافظهی تخصیصیافته توسط
PyObject_New،PyObject_NewVarیا توابع تخصیص مرتبط استفاده نکنید؛ به جای آن ازPyObject_Free()استفاده کنید.همچنین ببینید
PyObject_Free()معادلِ بدون GC این تابع است.
-
void PyObject_GC_UnTrack(void *op)¶
- قسمتی از ABI پایدار.
شیء op را از مجموعهی اشیای ظرفی که توسط جمعکننده پیگیری میشوند حذف میکند. توجه داشته باشید که
PyObject_GC_Track()میتواند دوباره روی این شیء فراخوانی شود تا آن را به مجموعهی اشیای پیگیریشده بازگرداند. آزادساز حافظهی (هندلرtp_dealloc) باید این تابع را برای این شیء پیش از آنکه هر یک از فیلدهای مورد استفادهی هندلرtp_traverseنامعتبر شوند، فراخوانی کند.
تغییر یافته در نسخهی 3.8: ماکروهای _PyObject_GC_TRACK() و _PyObject_GC_UNTRACK() از API عمومی C حذف شدهاند.
هندلر tp_traverse یک پارامتر تابع از این نوع میپذیرد:
-
typedef int (*visitproc)(PyObject *object, void *arg)¶
- قسمتی از ABI پایدار.
نوع تابع بازدیدکننده (visitor function) که به هندلر
tp_traverseپاس داده میشود. این تابع باید با یک شیء برای پیمایش بهعنوان object و پارامتر سوم هندلرtp_traverseبهعنوان arg فراخوانی شود. هستهی پایتون از چندین تابع بازدیدکننده برای پیادهسازی تشخیص زبالهی چرخهای استفاده میکند؛ انتظار نمیرود که کاربران نیازی به نوشتن توابع بازدیدکنندهی خودشان داشته باشند.
هندلر tp_clear باید از نوع inquiry باشد، یا در صورتی که شیء تغییرناپذیر است، NULL باشد.
-
typedef int (*inquiry)(PyObject *self)¶
- قسمتی از ABI پایدار.
ارجاعهایی را که ممکن است چرخههای ارجاع ایجاد کرده باشند رها کنید. اشیاء تغییرناپذیر نیازی به تعریف این متد ندارند، زیرا هرگز نمیتوانند بهطور مستقیم چرخههای ارجاع ایجاد کنند. توجه داشته باشید که شیء باید پس از فراخوانی این متد همچنان معتبر باشد (تنها
Py_DECREF()را روی یک ارجاع فراخوانی نکنید). زبالهروب در صورتی که تشخیص دهد این شیء در یک چرخه ارجاع درگیر است، این متد را فراخوانی خواهد کرد.
پیمایش¶
هندلر tp_traverse باید از نوع زیر باشد:
-
typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)¶
- قسمتی از ABI پایدار.
تابع پیمایش برای یک شیء جمعآوریشده در زبالهجمعکن، که توسط زبالهجمعکن برای تشخیص چرخههای ارجاع استفاده میشود. پیادهسازیها باید تابع visit را برای هر شیء که مستقیماً توسط self دربرگرفته شده است فراخوانی کنند، به طوری که پارامترهای ارسالی به visit شامل شیء محصورشده و مقدار arg ارسالشده به هندلر باشند. تابع visit نباید با یک آرگومان شیء
NULLفراخوانی شود. اگر visit یک مقدار غیرصفر برگرداند، آن مقدار باید بلافاصله بازگردانده شود.یک تابع
tp_traverseمعمولی، ماکروی کمکیPy_VISIT()را روی هر یک از اعضای نمونه فراخوانی میکند که از اشیای پایتون متعلق به نمونه هستند. برای نمونه، این یک تابع پیمایش (کمی قدیمیشده) برای کلاسthreading.localاست:static int local_traverse(PyObject *op, visitproc visit, void *arg) { localobject *self = (localobject *) op; Py_VISIT(Py_TYPE(self)); Py_VISIT(self->args); Py_VISIT(self->kw); Py_VISIT(self->dict); return 0; }
نکته
تابع
Py_VISIT()ایجاب میکند که پارامترهای visit و arg در تابعlocal_traverse()دارای این نامهای مشخص باشند؛ آنها را با هر نام دلخواهی نامگذاری نکنید.نمونههای انواع تخصیصیافته در هیپ ارجاعی به نوع خود را نگهداری میکنند. بنابراین تابع پیمایش آنها باید نوع را بازدید کند:
Py_VISIT(Py_TYPE(self));
بهعنوان جایگزین، نوع ممکن است با فراخوانی
tp_traverseاز یک سوپرکلاس تخصیصیافته در هیپ (یا یک نوع تخصیصیافتهی دیگر در هیپ، در صورت لزوم)، این مسئولیت را واگذار کند. اگر این کار را نکنند، ممکن است شیء نوع زبالهروبی نشود.اگر بیت
Py_TPFLAGS_MANAGED_DICTدر فیلدtp_flagsتنظیم شده باشد، تابع پیمایش باید تابعPyObject_VisitManagedDict()را به این شکل فراخوانی کند:int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg); if (err) { return err; }
فقط اعضایی که نمونه آنها را دارد (با داشتن ارجاعهای قوی به آنها) باید بازدید شوند. برای نمونه، اگر یک شیء از طریق جایگاه
tp_weaklistاز ارجاعهای ضعیف پشتیبانی کند، اشارهگری که از فهرست پیوندی پشتیبانی میکند (چیزی که tp_weaklist به آن اشاره میکند) نباید بازدید شود، زیرا نمونه مستقیماً مالک ارجاعهای ضعیف به خودش نیست.تابع پیمایش محدودیتی دارد:
هشدار
تابع پیمایش نباید هیچ اثر جانبی داشته باشد. پیادهسازیها نباید شمارههای ارجاع هیچیک از اشیای پایتون را تغییر دهند و همچنین نباید هیچ شیء پایتونی را، به صورت مستقیم یا غیرمستقیم، ایجاد یا نابود کنند.
این یعنی بیشتر توابع C API پایتون ممکن است استفاده نشوند، زیرا میتوانند یک استثنای جدید ایجاد کنند، یک ارجاع جدید به یک شیء نتیجه را برگردانند، یا منطق داخلی داشته باشند که از عوارض جانبی استفاده میکند. همچنین، مگر اینکه طور دیگری مستند شده باشد، توابعی که عوارض جانبی ندارند ممکن است در نسخههای آینده بدون هشدار پیدا کردن عوارض جانبی را شروع کنند.
برای فهرستی از توابع امن، به بخش جداگانه در ادامه مراجعه کنید.
نکته
فراخوانی
Py_VISIT()را میتوان برای آن دسته از اعضایی که ثابت شده نمیتوانند در چرخههای ارجاع شرکت کنند، رد کرد. در نمونهیlocal_traverseدر بالا، یک عضوself->keyنیز وجود دارد، اما این عضو فقط میتواندNULLیا یک رشتهی پایتون باشد و بنابراین نمیتواند بخشی از یک چرخه ارجاع باشد.از سوی دیگر، حتی اگر میدانید که یک عضو هرگز نمیتواند بخشی از یک چرخه باشد، به عنوان یک ابزار کمکی برای اشکالزدایی ممکن است بخواهید به هر حال آن را بازدید کنید تا تابع
get_referents()در ماژولgcآن را در بر بگیرد.نکته
تابع
tp_traverseرا میتوان از هر نخی فراخوانی کرد.زبالهروبی یک عملیات «متوقفکنندهی دنیا» است: حتی در پیادهسازیهای دارای نخبندی آزاد، هنگامی که کنترلکنندههای
tp_traverseاجرا میشوند، فقط یک حالت نخ متصل است.تغییر یافته در نسخهی 3.9: انتظار میرود انواع تخصیصیافته در هیپ،
Py_TYPE(self)را درtp_traverseبازدید کنند. در نسخههای قبلی پایتون، به دلیل bug 40217، انجام این کار ممکن است منجر به فروپاشی در زیرکلاسها شود.
برای سادهتر کردن نوشتن هندلرهای tp_traverse، یک ماکرو به نام Py_VISIT() فراهم شده است. به منظور استفاده از این ماکرو، پیادهسازی tp_traverse باید آرگومانهای خود را دقیقاً visit و arg نامگذاری کند:
-
Py_VISIT(o)¶
اگر عبارت PyObject* o برابر با
NULLنیست، کالبک visit را با آرگومانهای o و arg فراخوانی کنید. اگر visit یک مقدار غیرصفر برگرداند، آنگاه همان مقدار را برگردانید.این مورد تقریباً با این معادل است:
#define Py_VISIT(o) \ if (op) { \ int visit_result = visit(o, arg); \ if (visit_result != 0) { \ return visit_result; \ } \ }
توابع امن برای پیمایش¶
استفاده از توابع و ماکروهای زیر در یک هندلر tp_traverse امن است:
تابع visit که به
tp_traverseارسال میشودPy_TYPE(): اگر از یک هندلرtp_traverseفراخوانی شود، نتیجهیPy_TYPE()تا زمان پایان فراخوانی هندلر معتبر خواهد ماندPyObject_TypeCheck(),PyType_IsSubtype(),PyType_HasFeature()Py<type>_CheckوPy<type>_CheckExact-- برای نمونه،PyTuple_Check()
توابع «DuringGC»¶
توابع زیر فقط باید در یک هندلر tp_traverse استفاده شوند؛ فراخوانی آنها در زمینههای دیگر ممکن است پیامدهای ناخواستهای داشته باشد.
این توابع درست مانند همتایان خود بدون پسوند _DuringGC عمل میکنند، اما تضمین میشود که هیچ عارضهی جانبی نداشته باشند، در صورت بروز خطا هیچ استثنایی را تنظیم نمیکنند، و همانطور که در مستندات هر کدام به تفصیل آمده است، ارجاعهای امانتی را برمیگردانند/تنظیم میکنند.
توجه داشته باشید که این توابع ممکن است با خطا مواجه شوند (مقادیر NULL یا -1 را برگردانند)، اما از آنجا که استثنایی را تنظیم نمیکنند، هیچ اطلاعات خطایی در دسترس نیست. در برخی موارد، خطا از نتیجهی موفقیتآمیز NULL قابل تشخیص نیست.
-
void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls)¶
-
void *PyObject_GetItemData_DuringGC(PyObject *o)¶
-
void *PyType_GetModuleState_DuringGC(PyTypeObject *type)¶
-
void *PyModule_GetState_DuringGC(PyObject *module)¶
-
int PyModule_GetToken_DuringGC(PyObject *module, void **result)¶
- قسمتی از ABI پایدار از نسخهی 3.15.
برای کسب اطلاعات عمومی به توابع «DuringGC» مراجعه کنید.
اضافه شده در نسخهی 3.15.
-
int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result)¶
- قسمتی از ABI پایدار از نسخهی 3.15.
برای کسب اطلاعات عمومی به توابع «DuringGC» مراجعه کنید.
مقدار *result را بهجای یک ارجاع قوی، روی یک ارجاع امانتی تنظیم میکند. این ارجاع تا زمان پایان فراخوانی هندلر
tp_traverseمعتبر است.اضافه شده در نسخهی 3.15.
همچنین ببینید
-
PyObject *PyType_GetModule_DuringGC(PyTypeObject *type)¶
-
PyObject *PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token)¶
- مقدار بازگشتی: مرجع امانتی. قسمتی از ABI پایدار از نسخهی 3.15.
برای کسب اطلاعات عمومی به توابع «DuringGC» مراجعه کنید.
این توابع یک ارجاع امانتی برمیگردانند که تا زمان پایان فراخوانی هندلر
tp_traverseمعتبر است.اضافه شده در نسخهی 3.15.
همچنین ببینید
کنترل وضعیت زبالهروب¶
API زبان C توابع زیر را برای کنترل اجراهای زبالهروبی فراهم میکند.
-
Py_ssize_t PyGC_Collect(void)¶
- قسمتی از ABI پایدار.
در صورت فعال بودن زبالهروب، یک زبالهروبی کامل انجام میدهد. (توجه داشته باشید که
gc.collect()آن را بدون قید و شرط اجرا میکند.)تعداد اشیاء جمعآوریشده + اشیاء دسترسناپذیری که نمیتوان آنها را جمعآوری کرد را برمیگرداند. اگر زبالهروب غیرفعال باشد یا از قبل در حال جمعآوری باشد، بلافاصله
0را برمیگرداند. خطاهای رخداده در حین زبالهروبی بهsys.unraisablehookمنتقل میشوند. این تابع استثنایی ایجاد نمیکند.
-
int PyGC_Enable(void)¶
- قسمتی از ABI پایدار از نسخهی 3.10.
فعال کردن زبالهروب: مشابه
gc.enable(). وضعیت قبلی را برمیگرداند، ۰ برای غیرفعال و ۱ برای فعال.اضافه شده در نسخهی 3.10.
-
int PyGC_Disable(void)¶
- قسمتی از ABI پایدار از نسخهی 3.10.
جمعکننده زباله را غیرفعال میکند: مشابه
gc.disable(). وضعیت قبلی را برمیگرداند، 0 برای غیرفعال و 1 برای فعال.اضافه شده در نسخهی 3.10.
-
int PyGC_IsEnabled(void)¶
- قسمتی از ABI پایدار از نسخهی 3.10.
وضعیت زبالهروب را پرسوجو میکند: مشابه
gc.isenabled(). وضعیت فعلی را برمیگرداند، ۰ برای غیرفعال و ۱ برای فعال.اضافه شده در نسخهی 3.10.
پرسوجوی وضعیت زبالهروب¶
C-API رابط زیر را برای استعلام اطلاعات دربارهی زبالهروب فراهم میکند.
-
void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)¶
- این است API ناپایداراین ممکن است بدون هشدار در نسخههای جزئی تغییر کند.
callback دادهشده را روی تمام اشیای زندهی قابل زبالهروبی اجرا میکند. arg بدون تغییر به تمام فراخوانیهای callback پاس داده میشود.
هشدار
اگر اشیاء جدیدی توسط کالبک تخصیص داده (یا آزادسازی) شوند، تعریفنشده است که آیا بازدید خواهند شد یا خیر.
زبالهروبی در حین عملیات غیرفعال است. اجرای صریح یک زبالهروبی در کالبک ممکن است به رفتار تعریفنشده منجر شود؛ برای مثال، پیمایش همان اشیاء چندین بار یا اصلاً پیمایشنشدن آنها.
اضافه شده در نسخهی 3.12.
-
typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)¶
نوع تابع بازدیدکنندهای که باید به
PyUnstable_GC_VisitObjects()پاس داده شود. arg همان argی است که بهPyUnstable_GC_VisitObjectsپاس داده شده است. برای ادامهی پیمایش1را برگردانید و برای توقف پیمایش0را برگردانید. سایر مقادیر بازگشتی فعلاً رزرو شدهاند، بنابراین رفتار در صورت برگرداندن هر مقدار دیگر تعریفنشده است.اضافه شده در نسخهی 3.12.