المكدس أو الكومة في Go: كيف يؤثر تخصيص struct على GC

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

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

ابدأ الدورة

مقدمة: لماذا "مجرد struct" ليس بهذه البساطة؟

\n\n

هل سبق لك أن كتبت كودًا بلغة Go، وبينما تنظر إلى برنامجك، تساءلت: "أين يعيش هيكلي الآن؟ على المكدس السريع والمريح أم في مساحات الكومة الشاسعة تحت إشراف جامع القمامة؟". هذا السؤال لا يقلق المبتدئين فقط. على Stack Overflow، موضوع "Stack vs heap allocation of structs in Go, and how they relate to garbage collection" حصد آلاف المشاهدات. وهذا ليس من قبيل الصدفة.

\n\n

البساطة الظاهرية للغة Go تخفي آلية قوية - تحليل الهروب (escape analysis). إنه محقق المترجم الذي يقرر مصير كل كائن. يعتمد أداء تطبيقك وعبء العمل على GC (جامع القمامة) على قراره. في هذا البرنامج التعليمي خطوة بخطوة، سنرتدي قبعة المحقق ونفهم:

\n
    \n
  • لماذا يعيش هيكل واحد على المكدس بينما يعيش آخر في الكومة؟
  • \n
  • كيف المؤشرات والإغلاقات تكسر مثالية المكدس.
  • \n
  • كيف ستساعدك هذه المعرفة في كتابة كود سريع وفعال بلغة Go.
  • \n
\n\n

المكدس مقابل الكومة: نبذة مختصرة

\n

قبل الغوص في الكود، دعنا ننعش الذاكرة بالنظرية. لا تقلق، ستكون سريعة ومؤلمة.

\n\n

المكدس (Stack)

\n
    \n
  • السرعة: فائقة السرعة. تخصيص الذاكرة هو مجرد تحريك المؤشر لأسفل.
  • \n
  • دورة الحياة: مرتبطة بشكل صارم باستدعاء الدالة. تعيش طالما تعمل الدالة.
  • \n
  • الإدارة: تلقائية. "تحترق" عند الخروج من الدالة.
  • \n
  • الحجم: صغير وثابت لكل goroutine (عادة 2-4 كيلوبايت).
  • \n
\n\n

الكومة (Heap)

\n
    \n
  • السرعة: أبطأ. يتطلب البحث عن كتلة ذاكرة حرة.
  • \n
  • دورة الحياة: طالما أن هناك مراجع للكائن من البرنامج. قد يعيش أكثر من الدالة التي أنشأته.
  • \n
  • الإدارة: جامع القمامة (GC). يقوم بمسح الكومة، والعثور على الكائنات "الميتة" وتحرير الذاكرة. هذا يكلف موارد وحدة المعالجة المركزية (CPU).
  • \n
  • الحجم: كبير (محدود فقط بذاكرة نظام التشغيل).
  • \n
\n\n

القاعدة الذهبية في Go: "أبقِ البيانات على المكدس لأطول فترة ممكنة". هذا يقلل العبء على GC ويسرع البرنامج.

\n\n

تحليل الهروب: المحقق الرئيسي في Go

\n

في Go، لا تكتب new أو malloc لتحديد مكان وضع الكائن بشكل صريح. المترجم يقرر ذلك نيابة عنك. هذه العملية تسمى تحليل الهروب (escape analysis).

\n

ينظر إلى الكود الخاص بك ويطرح سؤالاً واحدًا: "هل يمكن للمرجع إلى هذا الكائن أن يغادر الدالة الحالية؟".

\n
    \n
  • إذا كانت الإجابة لا (الكائن لا يهرب) → يتم وضع الكائن على المكدس.
  • \n
  • إذا كانت الإجابة نعم (الكائن يهرب) → يتم وضع الكائن في الكومة.
  • \n
\n\n

دعنا نرى هذا عمليًا.

\n\n

مثال 1: هيكل آمن على المكدس

\n
package main\n\nimport "fmt"\n\ntype Point struct {\n    X, Y int\n}\n\n// createPoint تنشئ Point وتعيده بالقيمة.\nfunc createPoint(x, y int) Point {\n    p := Point{X: x, Y: y}\n    return p // يتم نسخ p عند الإرجاع\n}\n\nfunc main() {\n    p1 := createPoint(10, 20)\n    fmt.Println(p1)\n}\n
\n

في هذا المثال، p هو متغير محلي. يتم إنشاؤه داخل createPoint ويتم إرجاعه بالقيمة (يتم نسخه). لا يمكن استخدام الهيكل p نفسه خارج الدالة (نسخته تُستخدم في main). يرى تحليل الهروب هذا ويضع p بسعادة على المكدس. GC يستريح.

\n\n

مثال 2: الهروب عبر المؤشر

\n

والآن دعنا نغير سطرًا واحدًا فقط - نعيد المؤشر.

\n
package main\n\nimport "fmt"\n\ntype Point struct {\n    X, Y int\n}\n\n// createPointer تنشئ Point وتعيد مؤشرًا إليه.\nfunc createPointer(x, y int) *Point {\n    p := P                

المدونات

توصيات الكتب