لماذا نهتم بإدارة الذاكرة في PHP؟
\nمعظم مطوري PHP يعتبرون الذاكرة موردًا لا ينضب. تريد مصفوفة بمليون عنصر؟ تفضل. أنشأت كائنًا ثم نسيت أمره؟ لا بأس، PHP سينظفها بنفسه. لكن عندما يبدأ مشروعك بالانهيار مع خطأ Allowed memory size exhausted أو يتباطأ فجأة دون سبب واضح، يحين الوقت لفهم ما يحدث تحت الغطاء.
اليوم سنلقي نظرة داخل الصندوق الأسود المسمى "إدارة الذاكرة وجامع القمامة في PHP". ستتعلم كيف يحسب PHP المراجع، ولماذا تعتبر المراجع الدائرية مشكلة، وكيف تجعل جامع القمامة يعمل لصالحك وليس ضدك. هيا بنا!
\n\nكيف يدير PHP الذاكرة: من الطلب إلى الاستجابة
\nعلى عكس C أو C++ حيث تتحكم أنت في تخصيص الذاكرة وتحريرها، يعمل PHP عبر Zend Engine. كل عملية PHP لها مجموعة ذاكرة خاصة بها. عندما تكتب:
\n$data = [1, 2, 3];\nيخصص PHP ذاكرة للمصفوفة، ويخزن القيم، ويزيد ما يسمى refcount (عداد المراجع). طالما أن المتغير $data مشار إليه من مكان آخر، لا يتم تحرير الذاكرة.
\nولكن بمجرد أن تفعل:
\nunset($data);\nينخفض عداد المراجع إلى الصفر، ويعيد Zend Engine الذاكرة فورًا إلى المجموعة. الأمر بسيط وسريع. لكن هناك تفاصيل دقيقة...
\n\nحساب المراجع: refcount و is_ref
\nكل متغير في PHP هو بنية zval تخزن القيمة، والنوع، والعلم is_ref (هل هو مرجع؟) و refcount. لنرى مثالًا:
$a = "مرحبًا";\n$b = $a; // يزيد refcount إلى 2 (نسخ عند الكتابة)\n$b = "العالم"; // الآن $a و $b هما zval مختلفان، refcount لـ "مرحبًا" ينخفض إلى 1\n\nيسمى هذا copy-on-write (النسخ عند الكتابة). طالما أنك لا تغير القيمة، يوفر PHP الذاكرة باستخدام نفس المساحة. ولكن بمجرد تغيير المتغير، يتم إنشاء نسخة. ذكي، أليس كذلك؟
\n\nجامع القمامة (GC): عندما لا يكفي عداد المراجع
\nآلية عد المراجع العادية تعمل بشكل ممتاز في 99% من الحالات. لكن هناك مشكلة واحدة — المراجع الدائرية. تخيل أن لديك كائنين يشير كل منهما إلى الآخر:
\nclass 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 (كل منهما يشير إلى الآخر). رسميًا، هما غير قابلين للوصول من الكود، لكن الذاكرة لا تُحرر. هذه هي المراجع الدائرية.
إذا كان لديك العديد من هذه الكائنات، فستحدث تسريبات للذاكرة. لمكافحة هذا، ظهر في PHP منذ الإصدار 5.3 جامع قمامة كامل (GC).
\n\nكيف يعمل GC في PHP؟
\nجامع القمامة لا يعمل مع كل سطر كود. يتم تفعيله عندما يمتلئ المخزن المؤقت الجذري (مصفوفة المراجع الدائرية المحتملة). حجمه الافتراضي هو 10,000 عنصر. عندما يمتلئ المخزن، يشغل GC خوارزمية تقوم بـ:
\n- \n
- مسح الجذور — إيجاد جميع zval التي لديها refcount > 0 والتي قد تكون جزءًا من دورة. \n
- تقليل refcount لجميع العناصر الفرعية (كما لو لم تكن هناك مراجع). \n
- التحقق — إذا أصبح refcount صفرًا بعد التخفيض، فهذا يعني أنها "قمامة" ويمكن حذفها. \n
- استعادة refcount لتلك التي لم تكن قمامة، وتحرير الذاكرة. \n
تسمى هذه العملية garbage collection. يمكنك تشغيلها يدويًا:
\ngc_collect_cycles();\nلكن لا تفعل ذلك في كل مرة — فهي عملية مكلفة.
\n\nكيف تكتشف تسريبات الذاكرة في PHP؟
\nإذا كان سكريبتك يعمل لفترة طويلة (مثل عامل قائمة انتظار أو خفيضة)، تصبح التسريبات حرجة. إليك بعض الأدوات