پشتیبانی از زبالهروبی چرخهای¶
پشتیبانی پایتون از تشخیص و جمعآوری زبالههایی که شامل ارجاعهای چرخهای هستند، نیازمند پشتیبانی از سوی نوعهای شیء است که «ظرف»هایی برای اشیاء دیگر به شمار میروند و ممکن است خود آن اشیاء نیز ظرف باشند. نوعهایی که ارجاعی به اشیاء دیگر ذخیره نمیکنند، یا تنها ارجاعهایی به نوعهای اتمی (مانند اعداد یا رشتهها) ذخیره میکنند، نیازی به ارائه هیچ پشتیبانی صریحی برای زبالهروبی ندارند.
برای ایجاد یک نوع ظرف، فیلد 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_traverse باید از نوع زیر باشد:
-
typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)¶
- قسمتی از ABI پایدار.
تابع پیمایش برای یک شیء ظرف. پیادهسازیها باید تابع visit را برای هر شیءای که مستقیماً توسط self دربرگرفته شده است فراخوانی کنند، بهطوری که پارامترهای visit، شیء دربرگرفته و مقدار arg*ِ ارسالشده به هندلر باشند. تابع *visit نباید با آرگومان شیء
NULLفراخوانی شود. اگر visit مقدار غیرصفر برگرداند، آن مقدار باید بیدرنگ بازگردانده شود.تابع پیمایش نباید هیچ اثر جانبی داشته باشد. پیادهسازیها نباید شمارش ارجاع هیچیک از اشیاء پایتون را تغییر دهند و نباید هیچ شیء پایتونی ایجاد یا نابود کنند.
برای سادهسازی نوشتن هندلرهای tp_traverse، ماکروی Py_VISIT() فراهم شده است. برای استفاده از این ماکرو، پیادهسازی tp_traverse باید آرگومانهای خود را دقیقاً visit و arg نامگذاری کند:
-
Py_VISIT(o)¶
اگر PyObject* o برابر
NULLنباشد، کالبک visit با آرگومانهای o و arg فراخوانی میشود. اگر visit مقدار غیرصفری برگرداند، همان مقدار برگردانده میشود. با استفاده از این ماکرو، هندلرهایtp_traverseبه این شکل هستند:static int my_traverse(Noddy *self, visitproc visit, void *arg) { Py_VISIT(self->foo); Py_VISIT(self->bar); return 0; }
هندلر tp_clear باید از نوع inquiry باشد، یا در صورتی که شیء تغییرناپذیر است، NULL باشد.
-
typedef int (*inquiry)(PyObject *self)¶
- قسمتی از ABI پایدار.
ارجاعهایی را که ممکن است چرخههای ارجاع ایجاد کرده باشند رها کنید. اشیاء تغییرناپذیر نیازی به تعریف این متد ندارند، زیرا هرگز نمیتوانند بهطور مستقیم چرخههای ارجاع ایجاد کنند. توجه داشته باشید که شیء باید پس از فراخوانی این متد همچنان معتبر باشد (تنها
Py_DECREF()را روی یک ارجاع فراخوانی نکنید). زبالهروب در صورتی که تشخیص دهد این شیء در یک چرخه ارجاع درگیر است، این متد را فراخوانی خواهد کرد.
کنترل وضعیت زبالهروب¶
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.