إدارة الذاكرة في PHP: كيف يعمل جامع القمامة (GC) ولماذا لا ينام

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا نهتم بإدارة الذاكرة في PHP؟

\n

معظم مطوري PHP يعتبرون الذاكرة موردًا لا ينضب. تريد مصفوفة بمليون عنصر؟ تفضل. أنشأت كائنًا ثم نسيت أمره؟ لا بأس، PHP سينظفها بنفسه. لكن عندما يبدأ مشروعك بالانهيار مع خطأ Allowed memory size exhausted أو يتباطأ فجأة دون سبب واضح، يحين الوقت لفهم ما يحدث تحت الغطاء.

\n

اليوم سنلقي نظرة داخل الصندوق الأسود المسمى "إدارة الذاكرة وجامع القمامة في PHP". ستتعلم كيف يحسب PHP المراجع، ولماذا تعتبر المراجع الدائرية مشكلة، وكيف تجعل جامع القمامة يعمل لصالحك وليس ضدك. هيا بنا!

\n\n

كيف يدير PHP الذاكرة: من الطلب إلى الاستجابة

\n

على عكس C أو C++ حيث تتحكم أنت في تخصيص الذاكرة وتحريرها، يعمل PHP عبر Zend Engine. كل عملية PHP لها مجموعة ذاكرة خاصة بها. عندما تكتب:

\n
$data = [1, 2, 3];
\n

يخصص PHP ذاكرة للمصفوفة، ويخزن القيم، ويزيد ما يسمى refcount (عداد المراجع). طالما أن المتغير $data مشار إليه من مكان آخر، لا يتم تحرير الذاكرة.

\n

ولكن بمجرد أن تفعل:

\n
unset($data);
\n

ينخفض عداد المراجع إلى الصفر، ويعيد Zend Engine الذاكرة فورًا إلى المجموعة. الأمر بسيط وسريع. لكن هناك تفاصيل دقيقة...

\n\n

حساب المراجع: refcount و is_ref

\n

كل متغير في PHP هو بنية zval تخزن القيمة، والنوع، والعلم is_ref (هل هو مرجع؟) و refcount. لنرى مثالًا:

\n
$a = "مرحبًا";\n$b = $a;  // يزيد refcount إلى 2 (نسخ عند الكتابة)\n$b = "العالم"; // الآن $a و $b هما zval مختلفان، refcount لـ "مرحبًا" ينخفض إلى 1\n
\n

يسمى هذا copy-on-write (النسخ عند الكتابة). طالما أنك لا تغير القيمة، يوفر PHP الذاكرة باستخدام نفس المساحة. ولكن بمجرد تغيير المتغير، يتم إنشاء نسخة. ذكي، أليس كذلك؟

\n\n

جامع القمامة (GC): عندما لا يكفي عداد المراجع

\n

آلية عد المراجع العادية تعمل بشكل ممتاز في 99% من الحالات. لكن هناك مشكلة واحدة — المراجع الدائرية. تخيل أن لديك كائنين يشير كل منهما إلى الآخر:

\n
class Node {\n    public $next;\n}\n\n$a = new Node();\n$b = new Node();\n$a->next = $b;\n$b->next = $a;\n\nunset($a);\nunset($b);\n
\n

بعد unset، لا يزال كلا الكائنين لديهما refcount = 1 (كل منهما يشير إلى الآخر). رسميًا، هما غير قابلين للوصول من الكود، لكن الذاكرة لا تُحرر. هذه هي المراجع الدائرية.

\n

إذا كان لديك العديد من هذه الكائنات، فستحدث تسريبات للذاكرة. لمكافحة هذا، ظهر في PHP منذ الإصدار 5.3 جامع قمامة كامل (GC).

\n\n

كيف يعمل GC في PHP؟

\n

جامع القمامة لا يعمل مع كل سطر كود. يتم تفعيله عندما يمتلئ المخزن المؤقت الجذري (مصفوفة المراجع الدائرية المحتملة). حجمه الافتراضي هو 10,000 عنصر. عندما يمتلئ المخزن، يشغل GC خوارزمية تقوم بـ:

\n
    \n
  • مسح الجذور — إيجاد جميع zval التي لديها refcount > 0 والتي قد تكون جزءًا من دورة.
  • \n
  • تقليل refcount لجميع العناصر الفرعية (كما لو لم تكن هناك مراجع).
  • \n
  • التحقق — إذا أصبح refcount صفرًا بعد التخفيض، فهذا يعني أنها "قمامة" ويمكن حذفها.
  • \n
  • استعادة refcount لتلك التي لم تكن قمامة، وتحرير الذاكرة.
  • \n
\n

تسمى هذه العملية garbage collection. يمكنك تشغيلها يدويًا:

\n
gc_collect_cycles();
\n

لكن لا تفعل ذلك في كل مرة — فهي عملية مكلفة.

\n\n

كيف تكتشف تسريبات الذاكرة في PHP؟

\n

إذا كان سكريبتك يعمل لفترة طويلة (مثل عامل قائمة انتظار أو خفيضة)، تصبح التسريبات حرجة. إليك بعض الأدوات

المدونات

توصيات الكتب