
مشاركة بيانات فئة التطبيق (AppCDS) - ميزة JVM لتسريع بدء التشغيل وحفظ الذاكرة. بعد أن ظهرت في مهدها في HotSpot مرة أخرى في JDK 1.5 (2004) ، ظلت لفترة طويلة محدودة للغاية ، وحتى تجارية جزئية. فقط مع OpenJDK 10 (2018) تم إتاحته لمجرد البشر ، وفي نفس الوقت توسيع النطاق. وأصدرت مؤخرا جافا 13 حاول أن تجعل هذا التطبيق أكثر بساطة.
فكرة AppCDS هي "مشاركة" بمجرد تحميل الفصول بين مثيلات نفس JVM على نفس المضيف. يبدو أن هذا يجب أن يكون مفيدًا للخدمات الميكروية ، خاصةً "الفراريج" في Spring Boot مع الآلاف من فصول المكتبة ، لأنه الآن لن تحتاج إلى تحميل هذه الفئات (تحليلها والتحقق منها) في كل بداية لكل مثيل JVM ، ولن يتم تكرارها في الذاكرة. هذا يعني أن عملية الإطلاق يجب أن تصبح أسرع وأن استهلاك الذاكرة يجب أن يكون أقل. رائع ، أليس كذلك؟
كل شيء هكذا ، كل شيء هكذا. ولكن إذا كنت ، odnokhabryanin ، اعتدت أن لا تصدق علامات الجادة ، ولكن بأعداد وأمثلة محددة ، فمرحباً بك في kat - دعنا نحاول معرفة مدى حقيقة الأمر ...
بدلا من إخلاء المسئولية
قبل أن لا تكون دليلًا لاستخدام AppCDS ، ولكن ملخصًا لنتائج دراسة صغيرة. لقد كنت مهتمًا بفهم كيفية تطبيق وظيفة JVM هذه في مشروع العمل الخاص بي ، وحاولت تقييمها من منظور مطور المؤسسة ، مع توضيح النتيجة في هذه المقالة. لم يتضمن ذلك موضوعات مثل استخدام AppCDS على مسار الوحدة النمطية ، وتطبيق AppCDS على الأجهزة الظاهرية الأخرى (وليس HotSpot) ، وتعقيدات استخدام الحاويات. ولكن هناك جزء نظري لاستكشاف الموضوع ، بالإضافة إلى جزء تجريبي مكتوب بحيث يمكنك تكرار التجربة بنفسك. لم يتم تطبيق أي من النتائج في الإنتاج ، ولكن من يدري كيف سيكون شكل الغد ...
نظرية
مقدمة موجزة ل AppCDS
قد تكون قد حصلت على معرفة بهذا الموضوع في عدة مصادر ، على سبيل المثال:
- في مقال بقلم نيكولاي بارلوغ (بما في ذلك Java 13 الكعك ، ولكن بدون Spring Boot)
- في تقرير ومقال لفولكر سيمونيس (بدون جافا 13 ، ولكن مع التفاصيل)
- في تقرير مؤلف هذه السطور (بدون Java 13 ، ولكن مع التركيز على Spring Boot)
من أجل عدم الانخراط في الرواية ، سأسلط الضوء فقط على بضع نقاط مهمة لهذه المقالة.
أولاً ، AppCDS هو امتداد لميزة CDS التي ظهرت منذ فترة طويلة في HotSpot ، وجوهرها كما يلي:

لإحياء كلتا الفكرتين ، عليك القيام بما يلي (بعبارات عامة):
- احصل على قائمة بالفئات التي تريد مشاركتها بين مثيلات التطبيق
- دمج هذه الفئات في أرشيف مناسب لتعيين الذاكرة
- قم بتوصيل الأرشيف بكل مثيل للتطبيق عند بدء التشغيل
يبدو أن الخوارزمية ليست سوى 3 خطوات - خذها وقم بها. ولكن هنا تبدأ الأخبار ، كل أنواع الأشياء.
الشيء السيئ هو أنه في أسوأ الحالات ، يتحول كل عنصر من هذه العناصر إلى تشغيل JVM واحد على الأقل مع خياراته الخاصة ، مما يعني أن الخوارزمية بأكملها عبارة عن لعبة خفية من نفس النوع من الخيارات والملفات. هذا لا يبدو واعدا جدا ، أليس كذلك؟
ولكن هناك أخبار سارة: العمل على تحسين هذه الخوارزمية مستمر ، ومع كل إصدار من Java ، يصبح تطبيقه أسهل. لذلك على سبيل المثال:
- في OpenJDK 10 و 11 ، يمكنك تخطي الخطوة 1 إذا كنت ترغب في مشاركة فئات JDK الرئيسية فقط ، حيث تم تجميعها بالفعل بالنسبة لنا ووضعها في
$JAVA_HOME\lib\classlist
(1200 جهاز كمبيوتر شخصى.). - في OpenJDK 12 ، يمكنك تخطي الخطوة 2 ، لأنه إلى جانب قائمة الفئات ، فإن أرشيف التوزيع يتضمن أيضًا أرشيفًا جاهزًا معهم ، والذي يتم استخدامه خارج الصندوق ولا يتطلب اتصالًا صريحًا.
- في حال كنت ترغب في مشاركة كل شيء آخر (وعادة ما تريد فقط)
يوفر OpenJDK 13 محفوظات CDS الديناميكية - المحفوظات التي يتم جمعها أثناء تشغيل التطبيق وحفظها عند تشغيله. هذا يسمح لك بتقسيم النقطتين 1 و 2 إلى نقطة ليست حائرة للغاية (على الرغم من أن كل شيء ليس بهذه البساطة ، ولكن أكثر حول ذلك لاحقًا).
وبالتالي ، بغض النظر عن عملية إعداد AppCDS ، فإن الخطوات الثلاث المذكورة أعلاه تكون دائمًا وراءها ، فقط في بعض الحالات تكون محجبة.
كما لاحظت على الأرجح ، مع ظهور AppCDS ، فإن العديد من فئات التطبيقات تبدأ حياة مزدوجة: تعيش في وقت واحد في أماكنها السابقة (في معظم الأحيان ملفات JAR) وفي أرشيف مشترك جديد. في نفس الوقت ، يواصل المطور تغييره / إزالته / استكماله في نفس المكان ، ويأخذ JVM منهم من الجديد عند العمل. لا يحتاج المرء إلى أن يكون إلهياً لمعرفة خطر مثل هذا الموقف: إذا لم يحدث شيء ، فسوف تتآكل نسخ عاجلة أو آجلاً من الفصول ، وسوف نحصل على العديد من سحر "جرة JAR" النموذجية. من الواضح أن JVM لا يمكن أن تمنع تغييرات الفصل ، ولكن يجب أن تكون قادرة على اكتشاف التناقض في الوقت المناسب. ومع ذلك ، للقيام بذلك عن طريق المقارنة بين الفئات ، حتى عن طريق الاختبارات ، هي فكرة ؛ يمكن أن ينفي بقية مكاسب الإنتاجية. ربما هذا هو السبب في أن مهندسي JVM لم يختاروا الفئات الفردية ككائن مقارنة ، ولكن classpath بالكامل ، ووردوا في وثائق AppCDS: "يجب أن يكون مسار التطبيق عند إنشاء أرشيف مشترك هو نفسه (أو على الأقل بادئة) كما هو الحال مع عمليات إطلاق التطبيق اللاحقة."
لاحظ أن classpath المستخدم في وقت إنشاء الأرشيف يجب أن يكون هو نفسه (أو بادئة) classpath المستخدم في وقت التشغيل.
لكن هذا ليس بيانًا لا لبس فيه ، لأنه ، كما تتذكر ، يمكن تشكيل مسار الفصل بطرق مختلفة ، مثل:
- قراءة ملفات
.class
العارية من دلائل الحزمة المترجمة ،
على سبيل المثال java com.example.Main
- فحص الدلائل مع ملفات JAR عند استخدام wildcard ،
على سبيل المثال java -cp mydir/* com.example.Main
- سرد صريح لملفات JAR و / أو ZIP ،
على سبيل المثال java -cp lib1.jar;lib2.jar com.example.Main
(لا يشمل ذلك حقيقة أن classpath يمكن أيضًا تعيينه بشكل مختلف ، على سبيل المثال ، من خلال خيارات JVM -cp/-classpath/--class-path
، أو متغير بيئة CLASSPATH
أو سمة ملف JAR Class-Path
المراد تشغيله)
من بين هذه الطرق ، يتم دعم طريقة واحدة فقط في AppCDS - التعداد الصريح لملفات JAR. على ما يبدو ، شعر مهندسو HotSpot JVM أن مقارنة المسابقين في أرشيف AppCDS وفي التطبيق الذي تم إطلاقه سيكون سريعًا بدرجة كافية وموثوقية فقط إذا تم تحديدها بأكبر قدر ممكن من الوضوح - بقائمة شاملة شاملة.
يدعم CDS / AppCDS أرشفة الطبقات من ملفات JAR فقط.
من المهم أن نلاحظ هنا أن هذا البيان غير تكراري ، أي لا ينطبق على ملفات JAR داخل ملفات JAR (ما لم يكن حول Dynamic CDS ، انظر أدناه). هذا يعني أن دمى JAR المعتادة التي تصدرها Spring Boot لن تعمل بنفس الطريقة مع AppCDS العادية ، يجب عليك الجلوس.
هناك ميزة أخرى في عمل CDS وهي أن المحفوظات المشتركة يتم عرضها على الذاكرة بعناوين ثابتة (تبدأ عادةً عند 0x800000000
). هذا في حد ذاته ليس سيئًا ، ولكن نظرًا لأنه يتم تمكين العشوائية في تخطيط مساحة العنوان (ASLR) افتراضيًا في معظم أنظمة التشغيل ، فقد يكون نطاق الذاكرة المطلوب مشغولًا جزئيًا. ما يفعله HotSpot JVM في هذه الحالة هو الخيار الخاص - -Xshare
الذي يدعم ثلاث قيم:
-Xshare:on
- فرض CDS / AppCDS ؛ إذا كان النطاق مشغولا ، فإن JVM يخرج مع وجود خطأ. لا ينصح باستخدام هذا الوضع في الإنتاج ، حيث قد يؤدي ذلك إلى تعطل متقطع عند تشغيل التطبيقات.-Xshare:off
- (أنت) تبديل CDS / AppCDS ؛ تعطيل استخدام البيانات المشتركة بالكامل (بما في ذلك الأرشيفات المدمجة)-Xshare:auto
- السلوك الافتراضي لـ JVM عندما يستسلم بهدوء ويقوم بتحميل الفئات كالمعتاد في حالة استحالة تخصيص نطاق الذاكرة المطلوب
في وقت كتابة هذا المقال ، كانت أوراكل تعمل فقط لتهدئة مثل هذه المشكلات ، ولكن لم يتم تعيين رقم إصدار بعد.
هذه الخيارات مفيدة جزئيا لنا في وقت لاحق ، ولكن الآن دعونا نلقي نظرة على ...
تطبيقات AppCDS
هناك العديد من الطرق التي يمكنك استخدامها مع AppCDS. خرب حياتك تحسين عمل الخدمات الصغيرة. إنها تختلف اختلافًا كبيرًا في التعقيد والأرباح المحتملة ، لذلك من المهم تحديد أي منها سيتم مناقشته لاحقًا.
إن أبسط ما في الأمر هو عدم استخدام AppCDS ، بل فقط CDS - وذلك عندما تدخل فئات النظام الأساسي فقط في الأرشيف المشترك (انظر "مقدمة مختصرة عن AppCDS"). سنقوم بحذف هذا الخيار على الفور ، لأنه عندما يتم تطبيقه على الخدمات المصغرة في برنامج Spring Boot ، فإنه يحقق ربحًا قليل جدًا. يمكن ملاحظة ذلك من خلال نسبة عدد الفئات المشتركة في التوزيع العام باستخدام مثال على خدمة microservice حقيقية واحدة (انظر الجزء الأخضر):

الأمر الأكثر تعقيدًا ، ولكنه واعد ، هو استخدام AppCDS الكامل ، أي إدراج فصول المكتبة والتطبيق في نفس الأرشيف. هذه مجموعة كاملة من الخيارات المشتقة من مجموعات من عدد التطبيقات المشاركة وعدد الحالات. فيما يلي تقييمات المؤلف الشخصية لفوائد وصعوبات التطبيقات المختلفة لـ AppCDS.
انتبه:
- في التطبيق على تطبيق واحد في حالة واحدة (رقم 1) ، قد يتحول ربح الذاكرة إلى صفر أو حتى سلبي (خاصة عند القياس تحت Windows )
- يتطلب إنشاء الأرشيف المشترك الصحيح إجراءات ، لا يعتمد تعقيدها على عدد النسخ التي سيتم تشغيل التطبيق عليها بعد ذلك (مقارنة أزواج الخيارين 1-2 ورقم 3-4)
- في الوقت نفسه ، من الواضح أن الانتقال من حالة إلى أخرى يعطي زيادة في الربح لكلا المؤشرين ، لكنه لا يؤثر على تعقيد الإعداد.
في هذه المقالة ، لن نصل إلا إلى الخيار رقم 2 (من خلال رقم 1) ، لأنه بسيط بما يكفي للتعرف على AppCDS وفقط له دون حيل إضافية ، يمكننا استخدام أرشيف JEP-350 Dynamic CDS Archives ، الذي أريد أن أشعر به في العمل.
المحفوظات CDS الحيوي
إن JEP-350 Dynamic CDS Archives ، أحد الابتكارات الرئيسية في Java 13 ، مصمم لجعل AppCDS أسهل في الاستخدام. لكي تشعر بالتبسيط ، يجب أن تفهم أولاً التعقيد. اسمحوا لي أن أذكرك بأن خوارزمية التطبيق الكلاسيكية "النظيفة" لـ AppCDS تتكون من 3 خطوات: (1) الحصول على قائمة بالفئات المشتركة ، (2) إنشاء أرشيف منها ، و (3) تشغيل التطبيق مع الأرشيف المتصل. من هذه الخطوات ، الخطوة الثالثة فقط هي في الواقع مفيدة ، والباقي هو الإعداد لها فقط. على الرغم من أن الحصول على قائمة بالفصول (الخطوة رقم 1) قد يبدو بسيطًا للغاية (في بعض الحالات يكون اختياريًا) ، في الواقع عند العمل مع التطبيقات غير التافهة ، يبدو أنه الأصعب ، خاصة فيما يتعلق ببرنامج Spring Boot. لذلك ، يلزم وجود JEP-350 فقط من أجل القضاء على هذه الخطوة ، أو بالأحرى تشغيلها تلقائيًا. الفكرة هي أن JVM نفسها تضع قائمة بالفئات التي يحتاجها التطبيق ، ثم تشكل في حد ذاتها الأرشيف "الديناميكي" المزعوم منها. موافق ، هذا يبدو جيدا. ولكن المشكلة هي أنه أصبح من غير الواضح الآن عند أي نقطة لوقف تراكم الطبقات والمضي قدما لوضعها في الأرشيف. في السابق ، في AppCDS الكلاسيكية ، اخترنا هذه اللحظة بأنفسنا ويمكننا أن ندمج بين هذه الإجراءات لتغيير شيء ما في قائمة الفئات قبل تحويله إلى أرشيف. يحدث هذا الآن تلقائيًا وفقط في لحظة واحدة ، وقد اختار مهندسو JVM ، وربما ، خيار التسوية الوحيد - الإغلاق المنتظم لـ JVM. هذا يعني أنه لن يتم إنشاء الأرشيف حتى يتوقف التطبيق. هذا الحل له عدة عواقب مهمة:
- في حالة تعطل JVM ، لن يتم إنشاء الأرشيف ، بغض النظر عن مدى روعة قائمة الفئات التي تراكمت في ذلك الوقت (لا يمكنك استخراجها لاحقًا باستخدام الوسائل العادية).
- سيتم إنشاء الأرشيف فقط من تلك الفئات التي تمكنت من تحميلها أثناء جلسة التطبيق. بالنسبة لتطبيقات الويب ، هذا يعني أن إنشاء أرشيف عن طريق البدء والإيقاف في الحال ليس صحيحًا ، حيث أن العديد من الفئات المهمة لن تدخل في الأرشيف. من الضروري تنفيذ طلب HTTP واحد على الأقل للتطبيق (ومن الأفضل تشغيله بشكل صحيح في جميع السيناريوهات) حتى يتم تحميل جميع الفئات التي يستخدمها بالفعل.
يتمثل الاختلاف المهم بين الأرشيفات الديناميكية والثابتة في أنها تشكل دائمًا "وظيفة إضافية" على الأرشيفات الثابتة الأساسية ، والتي يمكن أن تكون إما محفوظات مدمجة في مجموعة توزيع Java أو يتم إنشاؤها بشكل منفصل بطريقة كلاسيكية من 3 خطوات.
Syntactically ، استخدام Dynamic CDS Archives يأتي إلى إطلاق JVM مع خيارين:
- التشغيل التجريبي باستخدام الخيار
-XX:ArchiveClassesAtExit=archive.jsa
، سيتم في النهاية إنشاء أرشيف ديناميكي (يمكنك تحديد أي مسار واسم) - تشغيل مفيد باستخدام الخيار
-XX:SharedArchiveFile=archive.jsa
، والذي سيستخدم الأرشيف الذي تم إنشاؤه مسبقًا
لا يختلف الخيار الثاني عن ربط الأرشيف الثابت العادي. ولكن إذا لم يكن الأرشيف الثابت الأساسي فجأة في الموقع الافتراضي (داخل JDK) ، فقد يتضمن هذا الخيار أيضًا إشارة إلى المسار إلى ذلك ، على سبيل المثال:
-XX:SharedArchiveFile=base.jsa:dynamic.jsa
(تحت Windows ، يجب أن يكون فاصل المسار هو الحرف "؛")
الآن أنت تعرف ما يكفي عن AppCDS حتى تتمكن من النظر إليها في العمل.
ممارسة
أرنب تجريبي
حتى لا يقتصر تطبيقنا AppCDS في التطبيق على HelloWorld نموذجي ، فسنأخذ كأساس للتطبيق الحقيقي على Spring Boot. غالبًا ما يتعين علي أنا وزملائي مشاهدة سجلات التطبيقات على خوادم الاختبار عن بُعد ومشاهدة "مباشر" ، تمامًا مثل كتابتها. لاستخدام هذا السجل الشامل في كثير من الأحيان (مثل ELK) غالبا ما يكون غير مناسب ؛ تنزيل ملفات السجل إلى ما لا نهاية - لفترة طويلة ، والنظر إلى إخراج وحدة التحكم باللون الرمادي أمر محبط. لذلك ، قمت بإنشاء تطبيق ويب يمكنه إخراج أي سجلات في الوقت الفعلي مباشرة إلى المستعرض ، وتلوين الخطوط حسب مستوى الأهمية (في نفس الوقت تنسيق XML) ، وتجميع العديد من السجلات في سجل واحد ، بالإضافة إلى الحيل الأخرى. يطلق عليه ANALOG (مثل "محلل السجل" ، على الرغم من أن هذا ليس صحيحًا) ويقع على GitHub . انقر على الصورة للتكبير:

من الناحية الفنية ، يعد هذا تطبيقًا على Spring Boot + Spring Integration ، يعمل من خلال الغطاء الذي يعمل عليه كل من tail
kubectl
و kubectl
(لدعم السجلات من الملفات وحاويات Docker وموارد Kubernetes ، على التوالي). يأتي في شكل ملف Spring Boot JAR الكلاسيكي "السميك". في وقت التشغيل ، يتم تعليق فصول ≈10K في ذاكرة التطبيق ، والتي تمثل الغالبية العظمى منها فصول Spring و JDK. من الواضح أن هذه الفئات نادراً ما تتغير ، مما يعني أنه يمكن وضعها في أرشيف مشترك وإعادة استخدامها في جميع حالات التطبيق ، مما يوفر الذاكرة ووحدة المعالجة المركزية.
تجربة واحدة
الآن دعونا نطبق المعرفة الحالية لـ AppCDS الديناميكي على الأرنب التجريبي. نظرًا لأن كل شيء معروف في المقارنة ، فسوف نحتاج إلى نقطة مرجعية - حالة البرنامج الذي سنقوم بمقارنة النتائج التي تم الحصول عليها أثناء التجربة.
ملاحظات تمهيدية
- جميع الأوامر الأخرى هي لنظام التشغيل Linux. الاختلافات لنظام التشغيل Windows و macOS ليست أساسية.
- يمكن أن تؤثر مجموعة JIT بشكل ملحوظ على النتائج ، ومن الناحية النظرية ، من أجل نقاء التجربة ، يمكن إيقاف تشغيلها (مع خيار
-Xint
، كما حدث في المقالة المذكورة) ، ولكن من أجل أقصى قدر من المصداقية ، تقرر عدم القيام بذلك. - تم الحصول على الأرقام التالية حول وقت البدء على خادم اختبار سريع. على الأجهزة العاملة ، تكون الأرقام المتشابهة ، كقاعدة عامة ، أكثر تواضعا ، لكن بما أننا لسنا مهتمين بالقيم المطلقة ، لكن بالنسب المئوية للزيادات ، فإننا نعتبر هذا الاختلاف غير ذي أهمية.
- حتى لا نذهب قبل الأوان إلى تعقيد قياس الذاكرة المشتركة ، في الوقت الحالي ، سنحذف الحصول على قراءات دقيقة بالبايت. بدلاً من ذلك ، نقدم مفهوم " CDS المحتملة " ، معبراً عنه كنسبة مئوية من عدد الفئات المشتركة إلى إجمالي عدد الفئات المحملة. هذا ، بالطبع ، هو كمية مجردة ، ولكن من ناحية أخرى ، فإنه يؤثر بشكل مباشر على استهلاك الذاكرة الفعلي ؛ بالإضافة إلى ذلك ، لا يعتمد تعريفه على نظام التشغيل على الإطلاق ، ولحسابه ، لا تكفي إلا السجلات.
نقطة مرجعية
اجعل هذه النقطة هي حالة تطبيق تم تنزيله حديثًا ، أي دون الاستخدام الواضح لأي AppCDS'ov وغيرها. لتقييمه ، نحتاج إلى:
قم بتثبيت OpenJDK 13 (على سبيل المثال ، توزيع Liberica المحلي ، ولكن ليس الإصدار الخفيف).
يجب أيضًا إضافته إلى متغير بيئة PATH أو إلى JAVA_HOME
، على سبيل المثال ، مثل هذا:
export JAVA_HOME=~/tools/jdk-13
قم بتنزيل ANALOG (وقت كتابة هذا التقرير ، كان الإصدار الأخير v0.12.1).
إذا لزم الأمر ، يمكنك تحديد ملف config/application.yaml
في المعلمة server.address
اسم المضيف الخارجي للوصول إلى التطبيق (بشكل افتراضي ، يتم تحديد localhost
هناك).
تمكين تسجيل تحميل فئة JVM.
للقيام بذلك ، يمكنك JAVA_OPTS
متغير البيئة JAVA_OPTS
بهذه القيمة:
export JAVA_OPTS=-Xlog:class+load=info:file=log/class-load.log
سيتم تمرير هذا الخيار إلى JVM ويطلب منه التعهد بمصدر كل فصل.
تشغيل اختبار التشغيل:
- قم بتشغيل التطبيق باستخدام البرنامج النصي
bin/analog
- افتح http: // localhost: 8083 في المستعرض وأزرار الوخزة والجرعات
- أوقف التطبيق عن طريق الضغط على
Ctrl+C
في وحدة تحكم البرنامج النصي bin/analog
خذ النتيجة (من الملفات الموجودة في log/
الدليل)
إجمالي عدد الفئات المحملة (بواسطة class-load.log
):
cat class-load.log | wc -l 10463
كم منهم تم تنزيلها من أرشيف مشترك (وفقًا لذلك):
grep -o 'source: shared' - class-load.log 1146
متوسط وقت البدء (بعد سلسلة من عمليات التشغيل ؛ بواسطة analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.5225
لذلك ، في هذه الخطوة ، كانت إمكانات CDS 1146/10463=0,1095
≈11 ٪ . إذا كنت مندهشًا من حيث جاءت الفئات المشتركة (بعد كل شيء ، لم ندرج بعد أي AppCDS) ، فأنا أذكرك أنه بدءًا من الإصدار الثاني عشر ، يتضمن JDK أرشيف CDS النهائي $JAVA_HOME/lib/server/classes.jsa
، المدمج بما لا يقل عن قائمة جاهزة بالفصول:
cat $JAVA_HOME/lib/classlist | wc -l 1170
الآن ، وبعد تقييم الحالة الأولية للتطبيق ، يمكننا تطبيق AppCDS عليه ، وبالمقارنة ، نفهم ما يعطيه ذلك.
التجربة الأساسية
بما أن الوثائق الموروثة إلينا ، لإنشاء أرشيف AppCDS ديناميكي ، فأنت بحاجة إلى تشغيل -XX:ArchiveClassesAtExit
تجريبية واحدة فقط من التطبيق مع خيار -XX:ArchiveClassesAtExit
. من الإصدار التالي ، يمكن استخدام الأرشيف والحصول على الربح. للتحقق من ذلك على نفس الأرنب التجريبي (AnaLog) ، تحتاج إلى:
أضف الخيار المحدد إلى الأمر run:
export JAVA_OPTS="$JAVA_OPTS -XX:ArchiveClassesAtExit=work/classes.jsa"
تمديد التسجيل:
export JAVA_OPTS="$JAVA_OPTS -Xlog:cds=debug:file=log/cds.log"
يفرض هذا الخيار عملية إنشاء أرشيف CDS ليتم تسجيله عند إيقاف التطبيق.
قم بإجراء نفس الاختبار كما هو الحال مع النقطة المرجعية:
- قم بتشغيل التطبيق باستخدام البرنامج النصي
bin/analog
- افتح http: // localhost: 8083 في المستعرض وأزرار الوخزة والجرعات
- أوقف التطبيق عن طريق الضغط على
Ctrl+C
في وحدة تحكم البرنامج النصي bin/analog
بعد ذلك ، يجب وضع قطعة قدم هائلة بكل أنواع التحذير في وحدة التحكم ، ويجب ملء log/cds.log
بالتفاصيل ؛ إنهم لا يهتمون بنا حتى الآن.
بدّل وضع الإطلاق من الإصدار التجريبي إلى المفيد:
export JAVA_OPTS="-XX:SharedArchiveFile=work/classes.jsa -Xlog:class+load=info:file=log/class-load.log -Xlog:class+path=debug:file=log/class-path.log"
نحن هنا لا JAVA_OPTS
متغير JAVA_OPTS
، لكننا نعيد مسحه بقيم جديدة تتضمن (1) باستخدام أرشيف مشترك ، (2) مصادر فئة تسجيل الدخول و (3) اختبارات مسار فئة التسجيل.
إجراء إطلاق مفيد للتطبيق وفقًا للمخطط من الفقرة 3.
خذ النتيجة (من الملفات الموجودة في log/
الدليل)
التحقق من تطبيق AppCDS بالفعل (حسب class-path.log
):
[0.011s][info][class,path] type=BOOT [0.011s][info][class,path] Expecting BOOT path=/home/upc/tools/jdk-13/lib/modules [0.011s][info][class,path] ok [0.011s][info][class,path] type=APP [0.011s][info][class,path] Expecting -Djava.class.path=/home/upc/tmp/analog/lib/analog.jar [0.011s][info][class,path] ok
تشير علامات ok
بعد السطور type=BOOT
و type=APP
إلى نجاح فتح وتحقق وتحميل أرشيفات CDS المدمجة والمطبقة على التوالي.
إجمالي عدد الفئات المحملة (بواسطة class-load.log
):
cat class-load.log | wc -l 10403
كم منهم تم تنزيلها من أرشيف مشترك (وفقًا لذلك):
grep -o 'source: shared' -c class-load.log 6910
متوسط وقت البدء (بعد سلسلة من عمليات التشغيل ؛ حسب ملف analog.log
):
grep -oE '\(JVM running for .+\)' analog.log | grep -oE '[0-9]\.[0-9]+' | awk '{ total += $1; count++ } END { print total/count }' 4.04167
ولكن في هذه الخطوة ، كانت إمكانات CDS بالفعل 6910/10403≈0,66
= 66 ٪ ، أي أنها زادت بنسبة 55 ٪ مقارنة بالنقطة المرجعية. في الوقت نفسه ، تم تخفيض متوسط وقت الإطلاق بمقدار (4,5225-4,04167)=0,48
ثانية ، أي البدء أسرع بنسبة .610.6٪ من القيمة الأولية.
تحليل النتائج
عنوان العمل لهذا البند هو: "لماذا القليل جدا؟"
نحن ، مثل ، فعلنا كل شيء وفقًا للتعليمات ، ولكن لم تكن جميع الفئات في الأرشيف. يؤثر رقمهم على وقت الإطلاق بما لا يقل عن القدرة الحاسوبية لآلة المجرب ، لذلك سنركز على هذا الرقم.
إذا كنت تتذكر ، فقد تجاهلنا ملف log/cds.log
تم إنشاؤه أثناء إيقاف التطبيق التجريبي بعد التشغيل التجريبي. في ملف HotSpot هذا ، لاحظت JVM بفصول تحذير لكل فئة لم تظهر في أرشيف CDS. فيما يلي العدد الإجمالي لهذه العلامات:
grep -o '[warning]' cds.log -c 3591
بالنظر إلى أنه تم ذكر 10K + فقط من الفئات في سجل class-load.log
وتم تحميل 66٪ منها من الأرشيف ، ليس من الصعب أن نفهم أن الفئات 3600 المدرجة في cds.log
هي 44٪ من "CDS" المفقودة. أنت الآن بحاجة لمعرفة سبب تخطيها.
إذا نظرت إلى سجل cds.log ، اتضح أن هناك أربعة أسباب فريدة لتخطي الفئات. فيما يلي أمثلة لكل منها:
Skipping org/springframework/web/client/HttpClientErrorException: Not linked Pre JDK 6 class not supported by CDS: 49.0 org/jrobin/core/RrdUpdater Skipping java/util/stream/Collectors$$Lambda$554: Unsafe anonymous class Skipping ch/qos/logback/classic/LoggerContext: interface org/slf4j/ILoggerFactory is excluded
من بين جميع الفصول الـ 3591 الفائتة ، توجد هذه الأسباب هنا بمثل هذا التردد:

نلقي نظرة فاحصة عليها:
Unsafe anonymous class
JVM “” , -, .
Not linked
, “” , , . , StackOverflow . , , “” () JAR- , AppCDS. , ( ).
Pre JDK 6 class
, CDS Java 5. class- , CDS . , , 6, Java, . - , runtime- (, slf4j).
Skipping ... : super class/interface ... is excluded
, “” . CDS', . :
[warning][cds] Pre JDK 6 class not supported by CDS: 49.0 org/slf4j/spi/MDCAdapter [warning][cds] Skipping ch/qos/logback/classic/util/LogbackMDCAdapter: interface org/slf4j/spi/MDCAdapter is excluded
استنتاج
CDS 100%.
, , , , , . .
JEP-310 , AppCDS JDK. . , . CDS (, , ) .
لاستنساخ الأرنب التجريبي (تشغيل AnaLog في عدة حالات) ، نحتاج إلى تغيير شيء ما في الإعدادات ؛ هذا سيسمح للعمليات المرفوعة بعدم "الكوع". بفضل Spring Boot ، يمكنك القيام بذلك دون تحرير أو نسخ أي ملفات ؛ يمكن تجاوز أي إعدادات بواسطة خيارات JVM. إعادة توجيه هذه الخيارات من متغير البيئة ANALOG_OPTS
يوفر سكريبت تشغيل ، تم إنشاؤه بواسطة Gradle.
export ANALOG_OPTS="-Djavamelody.enabled=false -Dlogging.config=classpath:logging/logback-console.xml" export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
JavaMelody, , , . TCP- ; .
, , JVM AppCDS . JAVA_OPTS
JVM Unified Logging Framework :
export JAVA_OPTS="-Xlog:class+load=info:file=log/class-load-%p.log -Xlog:class+path=debug:file=log/class-path-%p.log" export JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=work/classes.jsa"
%p
, JVM (PID). AppCDS , ( ).
, . . :
server.port
nodes.this.agentPort
, :
export ANALOG_OPTS="$ANALOG_OPTS -Dnodes.this.agentPort=7801 -Dserver.port=8091"
, ( ).
bin/analog
() http://localhost:8091 ,
PID ( ), :
pgrep -f analog 13792
pmap
( ):
pmap -XX 13792 | sed -n -e '2p;$p' Address Perm Offset Device Inode Size KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous LazyFree AnonHugePages ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked ProtectionKey VmFlagsMapping 3186952 1548 1548 328132 325183 3256 0 10848 314028 212620 314024 0 0 0 0 0 0 0 325183 0 KB
; .
1-4 (, ).
pmap
. CDS' . , , PSS:
The "proportional set size" (PSS) of a process is the count of pages it has in memory, where each page is divided by the number of processes sharing it. So if a process has 1000 pages all to itself, and 1000 shared with one other process, its PSS will be 1500.
, , “ ” . , .
PSS , :
, - :
, . AppCDS. , -XX:SharedArchiveFile=work/classes.jsa
-Xshare:off
, CDS . , .

:
PSS AppCDS CDS.
. , , HelloWorld- JVM CDS 2 , CDS. PSS CDS, . :
PSS AppCDS 2- ; 3- .
, , , . , AppCDS, , , 3- .
: , CDS? :
CDS/AppCDS JVM , PSS . , , pmap
, “” sed
'. :
pmap -X `pgrep -f analog` 14981:
( Mapping
) , “” . JVM ( libjvm.so
), ( libc-2.27.so
). :
For the Java VM, the read-only parts of the loaded shared libraries (ie libjvm.so
) can be shared between all the VM instances running at the same time. This explains why, taking together, the two VM's consume less memory (ie have a smaller memory footprint) than the simple sum of their single resident set sizes when running alone.
. , , . , , JVM , Java- . GeekOut:

, , , AppCDS , .. Java-. , JVM, , - .
VisualVM Metaspace AppCDS , :
AppCDS

AppCDS

, 128 Metaspace AppCDS 64.2 MiB / 8.96 MiB
≈7,2 , CDS . (. ) 66.4 MiB / 13.9 MiB
≈4,8 . , AppCDS , Metaspace. Metaspace, , CDS .
بدلا من الاستنتاج
Spring Boot AppCDS – JVM, .
- JEP-350 Dynamic CDS Archives – JDK 13.
- Spring Boot ó CDS ( ). , 100% - 66% . , ≈11% ( 15%, ).
- , 5- PSS ( ). , AppCDS , , 8% (PSS). , CDS, , . AppCDS .
- Metaspace, , AppCDS 5 , CDS.
, , AppCDS, , “killer feature”. Spring Boot. , , AppCDS . , , AppCDS Spring Boot. , …
by Nick Fewings on Unsplash