"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > إدارة الذاكرة في الصورة الأصلية GraalVM

إدارة الذاكرة في الصورة الأصلية GraalVM

تم النشر بتاريخ 2024-09-12
تصفح:716

تعد إدارة الذاكرة عنصرًا حاسمًا في تطوير برامج الكمبيوتر، وهي مكلفة بتخصيص الذاكرة واستخدامها وتحريرها بشكل فعال في التطبيقات. وتكمن أهميتها في تعزيز أداء البرامج وضمان استقرار النظام.

جمع القمامة

تعتبر مجموعة البيانات المهملة (GC) أمرًا محوريًا في لغات البرمجة المعاصرة مثل Java وGo. فهو يكتشف الذاكرة غير المستخدمة ويعيد تدويرها بشكل مستقل، وبالتالي يخفف حاجة المطورين لإدارة الذاكرة يدويًا. ظهر مفهوم GC في الأصل في لغة برمجة LISP في أواخر الخمسينيات من القرن الماضي، مما يمثل مقدمة لإدارة الذاكرة الآلية.

تشمل المزايا الرئيسية لإدارة الذاكرة الآلية ما يلي:

  • منع تسرب الذاكرة والاستفادة الفعالة من الذاكرة.
  • عمليات التطوير المبسطة وتعزيز استقرار البرنامج.

يعد فهم طبيعة "القمامة" في الذاكرة وتحديد المساحة القابلة للاسترداد أمرًا ضروريًا. في الفصول القادمة، سنبدأ باستكشاف المبادئ الأساسية لجمع القمامة.

خوارزمية العد المرجعي [جورج إي. كولينز 1966]

تقوم خوارزمية العد المرجعي بتعيين حقل في رأس الكائن لتتبع العدد المرجعي الخاص به. ويزداد هذا العدد مع كل مرجع جديد ويتناقص عند إزالة مرجع. عندما يصل العدد إلى الصفر، يصبح الكائن مؤهلاً لجمع البيانات المهملة.

خذ بعين الاعتبار الكود التالي:

قم أولاً بإنشاء سلسلة ذات عرض توضيحي للقيمة المشار إليها بواسطة d (الشكل 1).

String d = new String("demo");

Memory Management in GraalVM Native Image

الشكل 1 – بعد إنشاء السلسلة

ثم اضبط d على قيمة فارغة. العدد المرجعي للعرض التوضيحي هو صفر. في خوارزمية العد المرجعي، يجب استعادة ذاكرة العرض التوضيحي (الشكل 2).

d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.

Memory Management in GraalVM Native Image

الشكل 2 - عندما يتم إلغاء المرجع

تعمل خوارزمية عد المرجع أثناء تنفيذ البرنامج، وتتجنب أحداث Stop-The-World، التي توقف البرنامج مؤقتًا لجمع البيانات المهملة. ومع ذلك، فإن عيبه الرئيسي هو عدم القدرة على التعامل مع المراجع الدائرية (الشكل 3).

على سبيل المثال:

public class CircularReferenceDemo {

  public CircularReferenceDemo reference;
  private String name;

  public CircularReferenceDemo(String name) {
    this.name = name;
  }

  public void setReference(CircularReferenceDemo ref) {
    this.reference = ref;
  }

  public static void main(String[] args) {
    CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A");
    CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B");

    objA.setReference(objB);
    objB.setReference(objA);

    objA = null;
    objB = null;
  }
}

هنا، على الرغم من إلغاء المراجع الخارجية، فإن المراجع المتبادلة بين objA وobjB تمنع تجميع البيانات المهملة.

Memory Management in GraalVM Native Image

الشكل 3 – المراجع الدائرية

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

يتم تنفيذ هذه الخوارزمية عمليًا في لغة C من خلال استخدام std::shared_ptr. تم تصميمه لإدارة دورة حياة الكائنات المخصصة ديناميكيًا، حيث يقوم std::shared_ptr بأتمتة زيادة وتناقص أعداد المرجع عند إنشاء مؤشرات إلى الكائن أو تدميرها. يعد هذا المؤشر الذكي جزءًا من مكتبة C القياسية، مما يوفر إمكانات قوية لإدارة الذاكرة تقلل بشكل كبير من المخاطر المرتبطة بالمعالجة اليدوية للذاكرة. كلما تم نسخ std::shared_ptr، يزداد عدد المرجع الداخلي للكائن المُدار، مما يعكس المرجع الجديد. على العكس من ذلك، عندما يتم تدمير std::shared_ptr، أو يخرج عن النطاق، أو يتم إعادة تعيينه إلى كائن مختلف، ينخفض ​​عدد المرجع. تتم استعادة الذاكرة المخصصة تلقائيًا ويتم تدمير الكائن عندما يصل العدد المرجعي الخاص به إلى الصفر،
منع تسرب الذاكرة بشكل فعال من خلال ضمان عدم تخصيص أي كائن دون ضرورة.

خوارزمية تحليل إمكانية الوصول [1978]

تبدأ خوارزمية تحليل إمكانية الوصول عند جذور GC، مرورًا بالرسم البياني للكائنات. الكائنات التي لا يمكن الوصول إليها من هذه الجذور تعتبر غير قابلة للاسترداد ويتم استهدافها للتجميع.

كما هو موضح في الصورة أدناه، يجب أن تظل الكائنات الموجودة في الدائرة الزرقاء على قيد الحياة ويمكن إعادة تدوير الكائنات الموجودة في الدائرة الرمادية (الشكل 4).

Memory Management in GraalVM Native Image

الشكل 4 - تسرب الذاكرة

تعمل هذه الطريقة بشكل فعال على حل مشكلة المراجع الدائرية المتأصلة في خوارزمية عد المراجع. يتم تصنيف الكائنات التي لا يمكن الوصول إليها من جذور GC للتجميع.

عادةً ما تتضمن كائنات Java التي تعتبر جذور GC ما يلي:

  • المتغيرات المحلية ضمن نطاق الطريقة الحالية.
  • مواضيع جافا النشطة.
  • الحقول الثابتة من الفئات.
  • مراجع JNI من الكود الأصلي.

نظرة عامة على الصورة الأصلية لـ GraalVM

يقدم GraalVM مترجمًا مسبقًا (AOT)، والذي يترجم تطبيقات Java إلى ثنائيات مستقلة قابلة للتنفيذ تُعرف باسم GraalVM Native Images. تم تطوير هذه الثنائيات بواسطة Oracle Labs
تغليف فئات التطبيقات والمكتبات ومكونات وقت التشغيل مثل GC، مما يسمح بالعمليات بدون بيئة تشغيل Java (JRE).

تتضمن العملية تحليلًا ثابتًا لتحديد المكونات التي يمكن الوصول إليها، والتهيئة من خلال الكتل المنفذة، والانتهاء من خلال إنشاء لقطة لحالة التطبيق لترجمة التعليمات البرمجية الآلية اللاحقة.

أساسيات الركيزة VM

يعد Substrate VM جزءًا لا يتجزأ من مجموعة GraalVM، التي تنظمها Oracle Labs. إنه JVM محسّن لا يدعم التجميع المسبق (AOT) فحسب، بل يسهل أيضًا تنفيذ اللغات خارج Java، مثل JavaScript وPython وRuby وحتى اللغات الأصلية مثل C وC. في جوهره، يعمل Substrate VM كإطار عمل متطور يسمح لـ GraalVM بتجميع تطبيقات Java في ثنائيات أصلية مستقلة. لا تعتمد هذه الثنائيات على جهاز Java Virtual Machine (JVM) التقليدي لتنفيذها، مما يؤدي إلى تبسيط النشر و
العمليات التشغيلية.

إحدى الميزات الأساسية لـ Substrate VM هي أداة تجميع البيانات المهملة المتخصصة، والتي تم ضبطها بدقة للتطبيقات التي تتطلب زمن وصول منخفض والحد الأدنى من مساحة الذاكرة. يعد جامع البيانات المهملة هذا بارعًا في التعامل مع تخطيط الذاكرة الفريد ونموذج التشغيل المتميز للصور الأصلية، والتي تختلف بشكل كبير عن تطبيقات Java التقليدية التي تعمل على JVM القياسي. يعد غياب مترجم Just-In-Time (JIT) في الصور الأصلية لـ Substrate VM خيارًا استراتيجيًا يساعد في تقليل الحجم الإجمالي للملف القابل للتنفيذ. وذلك لأنه يلغي ضرورة تضمين مترجم JIT والبيانات الوصفية المرتبطة به، والتي تعتبر كبيرة الحجم والتعقيد.

علاوة على ذلك، بينما تم تطوير GraalVM باستخدام Java، فإن هذا يفرض قيودًا معينة، خاصة فيما يتعلق بالوصول إلى الذاكرة الأصلية. ترجع هذه القيود في المقام الأول إلى المخاوف الأمنية والحاجة إلى الحفاظ على التوافق عبر الأنظمة الأساسية المختلفة. ومع ذلك، يعد الوصول إلى الذاكرة الأصلية أمرًا ضروريًا لعمليات تجميع البيانات المهملة المثالية. ولمعالجة هذه المشكلة، يستخدم Substrate VM مجموعة من الواجهات المتخصصة التي تسهل التفاعلات الآمنة والفعالة مع الذاكرة الأصلية. تعد هذه الواجهات جزءًا من بنية GraalVM الأوسع وتمكن Substrate VM من إدارة الذاكرة بشكل فعال بطريقة تشبه اللغات ذات المستوى الأدنى مثل C، كل ذلك مع الحفاظ على أمان Java وسهولة إدارته.

من الناحية العملية، تجعل هذه الإمكانات Substrate VM أداة متعددة الاستخدامات للغاية تعمل على تحسين وظائف وكفاءة التطبيقات المجمعة باستخدام GraalVM. من خلال السماح للمطورين بـ
من خلال الاستفادة من نطاق أوسع من لغات البرمجة وتجميعها في ثنائيات أصلية فعالة، يدفع Substrate VM حدود ما يمكن تحقيقه باستخدام بيئات تطوير Java التقليدية. وهذا يجعلها رصيدًا لا يقدر بثمن لمشاريع تطوير البرمجيات الحديثة التي تتطلب أداءً عاليًا واستهلاكًا منخفضًا للموارد ودعمًا متعدد الاستخدامات للغات.

تتضمن العناصر الجديرة بالملاحظة في Substrate VM ما يلي:

  • الوصول إلى الذاكرة المبسطة عبر واجهات مثل مؤشر واجهة المؤشر لعمليات الذاكرة الأولية وواجهة WordBase WordBase للتعامل مع القيم بحجم الكلمة.

  • تقسيم الكومة إلى مقاطع تمت تهيئتها مسبقًا تحتوي على كائنات غير قابلة للتغيير ومقاطع وقت التشغيل لتخصيص الكائنات الديناميكية (الشكل 5).

Memory Management in GraalVM Native Image

الشكل 5 - إدارة الذاكرة في الصورة الأصلية

في وقت التشغيل، يحتوي ما يسمى بكومة الصور في Substrate VM على كائنات تم إنشاؤها أثناء عملية إنشاء الصورة. تتم تهيئة هذا القسم من الكومة مسبقًا ببيانات من قسم البيانات الثنائية القابلة للتنفيذ ويمكن الوصول إليه بسهولة عند بدء تشغيل التطبيق. تعتبر الكائنات الموجودة في كومة الصورة خالدة؛ وبالتالي، يتم التعامل مع المراجع الموجودة داخل هذه الكائنات كمؤشرات جذرية بواسطة
جامع القمامة. ومع ذلك، يقوم GC فقط بفحص أجزاء من كومة الصورة بحثًا عن مؤشرات الجذر، وتحديدًا تلك التي لم يتم وضع علامة عليها للقراءة فقط.

أثناء عملية الإنشاء، يتم وضع الكائنات المخصصة للقراءة فقط في قسم محدد للقراءة فقط من كومة الصورة. نظرًا لأن هذه الكائنات لن تحتوي أبدًا على مراجع للكائنات المخصصة في وقت التشغيل، فهي لا تحتوي على مؤشرات جذر، مما يسمح لـ GC بتجاوزها أثناء عمليات الفحص. وبالمثل، فإن الكائنات التي تتكون فقط من بيانات أولية أو صفائف من الأنواع البدائية تفتقر أيضًا إلى المؤشرات الجذرية. تعمل هذه السمة على تبسيط عملية جمع البيانات المهملة، حيث يمكن حذف هذه الكائنات من عمليات فحص GC.

في المقابل، تم تصميم كومة Java للاحتفاظ بالكائنات العادية التي يتم إنشاؤها ديناميكيًا أثناء وقت التشغيل. يخضع هذا الجزء من الكومة لجمع البيانات المهملة بشكل منتظم لاستعادة المساحة التي تشغلها الكائنات التي لم تعد قيد الاستخدام. تم تصميمه على شكل كومة أجيال مع آليات للشيخوخة، مما يسهل إدارة الذاكرة بكفاءة بمرور الوقت.

هذا التقسيم بين كومة الصور الخالدة التي تمت تهيئتها مسبقًا وكومة Java المُدارة ديناميكيًا يمكّن Substrate VM من تحسين استخدام الذاكرة وكفاءة جمع البيانات المهملة، مما يلبي الجوانب الثابتة والديناميكية لمتطلبات ذاكرة التطبيق.

قطعة كومة

في نموذج الكومة الخاص بـ Substrate VM، يتم تنظيم الذاكرة بشكل منهجي في هياكل تعرف باسم قطع الكومة. تشكل هذه القطع، التي يبلغ حجمها عادةً 1024 كيلو بايت بشكل افتراضي، مقطعًا مستمرًا من الذاكرة الافتراضية المخصصة فقط لتخزين الكائنات. الهيكل التنظيمي لهذه القطع عبارة عن قائمة مرتبطة حيث تمثل القطعة الخلفية الجزء الذي تمت إضافته مؤخرًا. مثل هذا النموذج
يسهل تخصيص الذاكرة وإدارة الكائنات بشكل فعال.

يتم تصنيف قطع الكومة هذه إلى نوعين: محاذاة وغير محاذاة. قطع الكومة المحاذاة قادرة على الاحتفاظ بكائنات متعددة بشكل مستمر. تسمح هذه المحاذاة برسم خرائط أبسط لـ
الكائنات إلى قطع الكومة الأصلية الخاصة بها، مما يجعل إدارة الذاكرة أكثر سهولة وكفاءة. في السيناريوهات التي يكون فيها ترقية الكائن ضروريًا - عادةً، أثناء تجميع البيانات المهملة و
تحسين الذاكرة - يتم نقل الكائن من موضعه الأصلي في قطعة الكومة الأصلية إلى قطعة الكومة المستهدفة الموجودة في "مساحة قديمة" محددة. يعد هذا الترحيل جزءًا من إستراتيجية إدارة الكومة للأجيال التي تساعد في تحسين عملية جمع البيانات المهملة عن طريق فصل الكائنات الصغيرة عن القديمة، وبالتالي تقليل الحمل أثناء دورات GC.

جامعي القمامة في الصورة الأصلية

تدعم GraalVM Native Image مختلف GCs المصممة خصيصًا لتلبية الاحتياجات المختلفة:

  • Serial GC: مُجمِّع افتراضي منخفض البصمة مناسب للتطبيقات ذات الترابط الواحد.

  • أداة تجميع البيانات المهملة G1: مصممة للتطبيقات متعددة الخيوط ذات أحجام الكومة الكبيرة، مما يعزز المرونة في إدارة التوليد.

  • Epsilon GC: مُجمِّع بسيط يتعامل مع التخصيص ولكنه يفتقر إلى الاسترداد، ويُفضل استخدامه للتطبيقات قصيرة العمر حيث يمكن التنبؤ بالاستخدام الكامل للكومة.

خاتمة

في الختام، يعمل Substrate VM على تحسين إدارة الذاكرة بشكل فعال داخل GraalVM من خلال دمج التقنيات المتقدمة مثل جمع البيانات المهملة المتخصصة وإدارة الكومة المنظمة. تعمل هذه الميزات، بما في ذلك قطع الكومة وقطاعات الذاكرة المنفصلة لأكوام الصور وجافا، على تبسيط عملية جمع البيانات المهملة وتحسين أداء التطبيق. نظرًا لأن Substrate VM يدعم مجموعة متنوعة من لغات البرمجة ويجمعها في ثنائيات أصلية فعالة، فإنه يعرض كيف يمكن لأطر عمل JVM الحديثة أن تمتد إلى ما هو أبعد من الحدود التقليدية لتعزيز كفاءة التنفيذ والمتانة في بيئات التطبيقات المتنوعة. يضع هذا النهج معيارًا عاليًا للتطورات المستقبلية في تكنولوجيا الأجهزة الافتراضية ونشر التطبيقات.

بيان الافراج تم نشر هذه المقالة على: https://dev.to/yanev/memory-management-in-graalvm-native-image-4nbe?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
أحدث البرنامج التعليمي أكثر>

تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.

Copyright© 2022 湘ICP备2022001581号-3