إدارة ذاكرة Java

إشعار

يعد Azure Spring Apps هو الاسم الجديد لخدمة Azure Spring Cloud. رغم أن الخدمة تحمل اسماً جديداً، سترى الاسم القديم في بعض الأماكن لفترة من الوقت بينما نعمل على تحديث الأصول مثل لقطات الشاشة، ومقاطع الفيديو، والرسوم التخطيطية.

تنطبق هذه المقالة على: ✔️ Basic/Standard ✔️ Enterprise

توضح هذه المقالة المفاهيم المختلفة المتعلقة بإدارة ذاكرة Java لمساعدتك على فهم سلوك تطبيقات Java المستضافة في Azure Spring Apps.

نموذج ذاكرة Java

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

ذاكرة كومة الذاكرة المؤقتة

تخزن ذاكرة كومة الذاكرة المؤقتة جميع مثيلات الفئة والمصفوفات. يحتوي كل جهاز Java ظاهري (JVM) على منطقة كومة كومة واحدة فقط، والتي تتم مشاركتها بين مؤشرات الترابط.

يمكن أن يلاحظ Spring Boot Actuator قيمة ذاكرة كومة الذاكرة المؤقتة. يأخذ Spring Boot Actuator قيمة كومة الذاكرة المؤقتة كجزء من jvm.memory.used/committed/max. لمزيد من المعلومات، راجع قسم jvm.memory.used/committed/max في أدوات لاستكشاف مشكلات الذاكرة وإصلاحها.

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

  • الجيل الشاب: يتم تخصيص جميع الأشياء الجديدة والسن في جيل الشباب.

    • مساحة Eden: يتم تخصيص كائنات جديدة في مساحة Eden.
    • مساحة الناجين: سيتم نقل الكائنات من إدن إلى مساحة الناجين بعد النجاة من دورة واحدة لجمع القمامة. يمكن تقسيم مساحة الناجين إلى جزأين: s1 وs2.
  • الجيل القديم: يسمى أيضا مساحة محيازة. وسيتم نقل الأشياء التي ظلت في أماكن الناجين لفترة طويلة إلى الجيل القديم.

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

ذاكرة غير كومة الذاكرة المؤقتة

يتم تقسيم الذاكرة غير كومة الذاكرة المؤقتة إلى الأجزاء التالية:

  • جزء الذاكرة غير كومة الذاكرة المؤقتة التي حلت محل الجيل الدائم (أو permGen) بدءا من Java 8. يراقب Spring Boot Actuator هذا القسم ويأخذه كجزء من jvm.memory.used/committed/max. بمعنى آخر، jvm.memory.used/committed/max هو مجموع ذاكرة كومة الذاكرة المؤقتة وجزء permGen السابق من الذاكرة غير كومة الذاكرة المؤقتة. يتكون الجيل الدائم السابق من الأجزاء التالية:

    • Metaspace، الذي يخزن تعريفات الفئة التي تم تحميلها بواسطة محملات الفئة.
    • مساحة فئة مضغوطة، وهي لمؤشرات الفئة المضغوطة.
    • ذاكرة التخزين المؤقت للتعليمات البرمجية، التي تخزن التعليمات البرمجية الأصلية التي تم تجميعها بواسطة JIT.
  • ذاكرة أخرى مثل مكدس مؤشر الترابط، والذي لم تتم ملاحظته بواسطة Spring Boot Actuator.

الذاكرة المباشرة

الذاكرة المباشرة هي الذاكرة الأصلية المخصصة من قبل java.nio.DirectByteBuffer، والتي تستخدم في مكتبات الجهات الخارجية مثل nio وgzip.

لا يلاحظ Spring Boot Actuator قيمة الذاكرة المباشرة.

يلخص الرسم التخطيطي التالي نموذج ذاكرة Java الموضح في القسم السابق.

رسم تخطيطي يوضح نموذج ذاكرة Java.

تجميع البيانات المهملة ل Java

هناك ثلاثة مصطلحات تتعلق بمجموعة البيانات المهملة ل Java (GC): "GC الثانوية" و"GC الرئيسية" و"GC الكاملة". هذه المصطلحات غير محددة بوضوح في مواصفات JVM. هنا، نعتبر أن "الرئيس GC" و"GC الكامل" مكافئان.

يتم تنفيذ GC الثانوي عندما تكون مساحة Eden ممتلئة. فإنه يزيل جميع الأشياء الميتة في جيل الشباب وينقل الكائنات الحية من الفضاء Eden إلى s1 من مساحة الناجين، أو من s1 إلى s2.

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

يؤثر الحد الأقصى لحجم كومة الذاكرة المؤقتة على تكرار GC الثانوي وGC الكامل. يؤثر الحد الأقصى لمساحة التعريف والحد الأقصى لحجم الذاكرة المباشر على GC الكامل.

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

يمكن جمع Metaspace والذاكرة المباشرة فقط بواسطة GC الكامل. عندما تكون مساحة التعريف أو الذاكرة المباشرة ممتلئة، سيحدث GC الكامل.

تكوينات ذاكرة Java

تصف الأقسام التالية الجوانب الهامة لتكوين ذاكرة Java.

تعبئة Java في حاويات

تعمل التطبيقات في Azure Spring Apps في بيئات الحاوية. لمزيد من المعلومات، راجع تعبئة تطبيقات Java في حاويات.

خيارات JVM المهمة

يمكنك تكوين الحد الأقصى لحجم كل جزء من الذاكرة باستخدام خيارات JVM. يمكنك تعيين خيارات JVM باستخدام أوامر Azure CLI أو من خلال مدخل Microsoft Azure. لمزيد من المعلومات، راجع قسم تعديل التكوينات لإصلاح المشكلات في أدوات لاستكشاف مشكلات الذاكرة وإصلاحها.

تصف القائمة التالية خيارات JVM:

  • تكوين حجم كومة الذاكرة المؤقتة

    • -Xms تعيين حجم كومة الذاكرة المؤقتة الأولي حسب القيمة المطلقة.
    • -Xmx تعيين الحد الأقصى لحجم كومة الذاكرة المؤقتة حسب القيمة المطلقة.
    • -XX:InitialRAMPercentage تعيين حجم كومة الذاكرة المؤقتة الأولي حسب النسبة المئوية لحجم كومة الذاكرة المؤقتة / حجم ذاكرة التطبيق.
    • -XX:MaxRAMPercentage تعيين الحد الأقصى لحجم كومة الذاكرة المؤقتة حسب النسبة المئوية لحجم كومة الذاكرة المؤقتة / حجم ذاكرة التطبيق.
  • تكوين حجم الذاكرة المباشر

    • -XX:MaxDirectMemorySize تعيين الحد الأقصى لحجم الذاكرة المباشرة حسب القيمة المطلقة. لمزيد من المعلومات، راجع MaxDirectMemorySize في وثائق Oracle.
  • تكوين حجم مساحة التعريف

    • -XX:MaxMetaspaceSize تعيين الحد الأقصى لحجم مساحة التعريف حسب القيمة المطلقة.

الحد الأقصى لحجم الذاكرة الافتراضي

تصف الأقسام التالية كيفية تعيين الحد الأقصى الافتراضي لأحجام الذاكرة.

الحد الأقصى لحجم كومة الذاكرة المؤقتة الافتراضي

تعين Azure Spring Apps الحد الأقصى الافتراضي لحجم ذاكرة كومة الذاكرة المؤقتة إلى حوالي 50٪-80٪ من ذاكرة التطبيق لتطبيقات Java. على وجه التحديد، تستخدم Azure Spring Apps الإعدادات التالية:

  • إذا كانت ذاكرة < التطبيق 1 غيغابايت، فسيكون الحد الأقصى لحجم كومة الذاكرة المؤقتة الافتراضي 50٪ من ذاكرة التطبيق.
  • إذا كان 1 غيغابايت <= ذاكرة < التطبيق 2 غيغابايت، فسيكون الحد الأقصى لحجم كومة الذاكرة المؤقتة الافتراضي 60٪ من ذاكرة التطبيق.
  • إذا كان 2 غيغابايت <= ذاكرة < التطبيق 3 غيغابايت، فسيكون الحد الأقصى لحجم كومة الذاكرة المؤقتة الافتراضي 70٪ من ذاكرة التطبيق.
  • إذا كان 3 غيغابايت <= ذاكرة التطبيق، فسيكون الحد الأقصى لحجم كومة الذاكرة المؤقتة الافتراضي 80٪ من ذاكرة التطبيق.

الحد الأقصى الافتراضي لحجم الذاكرة المباشرة

عندما لا يتم تعيين الحد الأقصى لحجم الذاكرة المباشرة باستخدام خيارات JVM، يقوم JVM تلقائيا بتعيين الحد الأقصى لحجم الذاكرة المباشرة إلى القيمة التي تم إرجاعها بواسطة Runtime.getRuntime.maxMemory(). هذه القيمة تساوي تقريبا الحد الأقصى لحجم ذاكرة كومة الذاكرة المؤقتة. لمزيد من المعلومات، راجع ملف VM.java JDK 8.

تخطيط استخدام الذاكرة

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

يعتمد حجم مساحة التعريف على تعقيد التعليمات البرمجية الخاصة بك، مثل عدد الفئات.

يعتمد حجم الذاكرة المباشر على معدل النقل واستخدامك لمكتبات الجهات الخارجية مثل nio وgzip.

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

  • إجمالي الذاكرة (2048M)
  • ذاكرة كومة الذاكرة المؤقتة: Xmx هو 1433.6M (70٪ من إجمالي الذاكرة). القيمة المرجعية لاستخدام الذاكرة اليومية هي 1200M.
    • جيل الشباب
      • مساحة الناجين (S0، S1)
      • مساحة Eden
    • الجيل القديم
  • ذاكرة غير كومة الذاكرة المؤقتة
    • الجزء الذي تمت ملاحظته (الذي لاحظه Spring Boot Actuator)
      • Metaspace: القيمة المرجعية للاستخدام اليومي هي 50M-256M
      • ذاكرة التخزين المؤقت للتعليمات البرمجية
      • مساحة فئة مضغوطة
    • الجزء الذي لم تتم ملاحظته (لم تتم ملاحظته بواسطة Spring Boot Actuator): القيمة المرجعية للاستخدام اليومي هي 150M-250M.
      • مكدس مؤشر الترابط
      • GC ورمز داخلي وغير ذلك
  • الذاكرة المباشرة: القيمة المرجعية للاستخدام اليومي هي 10M-200M.

يوضح الرسم التخطيطي التالي نفس المعلومات. الأرقام باللون الرمادي هي القيم المرجعية لاستخدام الذاكرة اليومية.

رسم تخطيطي لتخطيط الذاكرة النموذجي لتطبيقات بحجم 2 غيغابايت.

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

Java OOM

OOM يعني أن التطبيق نفاد الذاكرة. هناك مفهومان مختلفان: حاوية OOM وJVM OOM. لمزيد من المعلومات، راجع مشكلات إعادة تشغيل التطبيق الناتجة عن مشكلات نفاد الذاكرة.

(راجع أيضًا )