من مقابلة رائعة مع هابري: "سيمون ريتر هو شخص عمل على جافا منذ البداية ويستمر في القيام بذلك كنائب المدير التقني لأزول ، وهي شركة تعمل على آلة Zing JVM الافتراضية وأحد أفضل جامعي القمامة ، C4 (Continuously Concurrent Compacting جامع) »
فيما يلي ترجمة لمقالته حول ميزات JDK 12 الجديدة وبعض الصعوبات التي قد تواجهها عند الترحيل إلى بنية جديدة.
كتبت العديد من منشورات المدونة التي تسرد جميع التغييرات لكل من أحدث إصدارات Java ( JDK 10 ، JDK 11 ). سأستكشف الآن الجانب المظلم من JDK 12 ، مع التركيز على بعض المزالق التي يمكن أن تسبب مشاكل إذا كنت ترغب في نقل التطبيق إلى هذا الإصدار.

يحتوي JDK 12 على أقل عدد من الميزات الجديدة من جميع إصدارات Java حتى الآن (لقد عدت 109 في JDK 10 و 90 في JDK 11). هذا ليس سيئًا - نظرًا لوجود دورات إطلاق ، ستتضمن بعض الإصدارات مزيدًا من التغييرات وبعضها أقل.
سوف أقسم الوظائف الجديدة إلى مناطق منطقية واضحة: Java والمكتبات و JVM ووظائف JDK الأخرى.
تغييرات اللغة
إن الوظيفة التي أراها I (وافترض أن العديد من الأشخاص الآخرين) ستراعي الأكثر وضوحًا في JDK 12 هي بيان التبديل الجديد ( JEP 325 ). هذا هو أيضًا تغيير اللغة الأول الذي يتم استخدامه كدالة "للمعاينة". تم تقديم فكرة "المعاينة" في أوائل عام 2018 كجزء من JEP 12 . هذه في الأساس طريقة لتمكين إصدارات بيتا من الميزات الجديدة باستخدام خيارات سطر الأوامر. باستخدام المعاينة ، لا يزال من الممكن إجراء تغييرات بناءً على ملاحظات المستخدم ، وفي أسوأ الحالات ، قم بإزالة الوظيفة بالكامل إذا لم يتم استلامها بشكل صحيح. مفتاح معاينة الوظائف هو أنها غير مضمنة في مواصفات Java SE. حول التبديل الجديد هناك ترجمة جيدة للغاية على حبري.
في JDK 12 ، أصبح المحول تعبيرًا يقيم "محتواه" لإنتاج نتيجة. سأوضح على الفور أن هذا لا يؤثر على التوافق مع الإصدارات السابقة ، لذلك لن تحتاج إلى تغيير أي رمز يستخدم التبديل كمشغل.
سأستخدم المثال من JEP ، لأنه بسيط وواضح:
التبديل القديمint numLetters; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: numLetters = 6; break; case TUESDAY: numLetters = 7; break; case THURSDAY: case SATURDAY: numLetters = 8; break; case WEDNESDAY: numLetters = 9; break; default: throw new IllegalStateException("Huh? " + day); }
كما ترون ، نقوم بتعيين يوم من أيام الأسبوع على اسم day
المتغير ، ثم numLetters
بتعيين قيمة numLetters
. الآن بعد أن أصبح رمز التبديل عاملًا ، يمكننا أن نقوم بتعيين مرة واحدة (مما يقلل بشكل كبير من احتمال وجود رمز خطأ) باستخدام نتيجة بيان التبديل:
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Huh? " + day); };
ستلاحظ بسرعة تغييرين في بناء الجملة. عثر مطورو OpenJDK على وظيفة بناء جملة غير معروفة تسمى قائمة مفصولة بفواصل. أيضا عامل التعبير lambda ->
يجعل إرجاع القيمة أسهل. لا يزال بإمكانك استخدام break
بقيمة إذا كنت تريد ذلك حقًا. هناك العديد من التفاصيل الأخرى حول هذه الميزة ، ولكن من المحتمل أن يكون من السهل قراءة JEP.
المكتبات
هناك تغيير واحد أجده مفيدًا للغاية. هناك أيضا عدد من الثانوية.
جامع teeing
يحتوي Stream API ، كالمعتاد ، على أداة تجميع جديدة ، يتم توفيرها بواسطة فئة الأداة المساعدة Collectors. يمكن الحصول على أداة تجميع جديدة باستخدام طريقة teeing()
. يأخذ جامع teeing ثلاث حجج: جامعان و bifunction. لفهم عمل هذا المجمع ، أوصي بهذا المقال عن حبري .
لفهم كيف يفعل هذا ، قمت برسم مخطط:

يتم تمرير جميع القيم من دفق الإدخال إلى كل جامع. يتم تمرير نتيجة كل جامع كوسيطات إلى BiFunction وتوليد النتيجة النهائية.
مثال بسيط هو حساب متوسط القيمة (نعم ، أعلم أن هناك بالفعل جامعي لهذا ، مثل averagingInt()
، ولكن هذا مثال بسيط للمساعدة في فهم المفهوم).
double average = Stream.of(1, 4, 2, 7, 4, 6, 5) .collect(teeing( summingDouble(i -> i), counting(), (sum, n) -> sum / n) );
يقوم المجمع الأول بحساب مجموع دفق الإدخال ، والثاني - عدد العناصر. تقسم BiFunction المجموع على عدد العناصر للحصول على القيمة المتوسطة.
java.io
InputStream skipNBytes(long n)
- يتخطى ويتجاهل بالضبط بايت n من دفق الإدخال InputStream. إذا كان n هو صفر أو أقل ، فلن يتم تخطي وحدات البايت.
java.lang
ظهرت حزمة جديدة ، java.lang.constant ، والتي تعد جزءًا من واجهة برمجة تطبيقات JVM الثابتة ، JEP 334 .
يحتوي كل ملف فئة Java على تجمع ثابت يخزّن المعاملات للحصول على تعليمات bytecode في الفصل الدراسي. يصعب على المطورين معالجة ملفات الفصل بسبب مشاكل في تحميل الفئات. توفر واجهة برمجة تطبيقات JVM الثابتة أنواع مرجعية رمزية لوصف كل شكل من أشكال ثابت (فئة ، ثابت قابل للتحميل ، MethodHandle
، MethodHandle
ثابت ، MethodType
ثابت).
كما أثرت في العديد من الطبقات الأخرى. تحتوي جميع الفئات التالية الآن على describeConstable()
طريقة:
- فئة
- مزدوج
- التعداد
- عوامة
- عدد صحيح
- طويل
- سلسلة
- MethodHandle
- MethodType
- VarHandle
كوني بريطاني ، أجد أنه أمر مضحك للغاية. مصطلح كونستابل ، describeConstable
كونستابل يستخدم منذ القرن الحادي عشر ، وهذه هي الطريقة التي نشير فيها إلى ضباط الشرطة. وهو أيضًا اسم الفنان الشهير في القرن الثامن عشر ، جون كونستابل. هذا يجعلني أتساءل عما إذا كانت طريقة describeTurner()
ستكون في إصدار مستقبلي. من الواضح ، أنه في هذه الحالة هو اختصار للطاولة Constant Table
، لا يتعلق بموظف قانوني أو رسام المناظر الطبيعية.
تتضمن الفئات التالية الآن أسلوب resolveConstantDesc()
:
- مزدوج
- Enum.EnumDesc
- عوامة
- عدد صحيح
- طويل
- سلسلة
java.lang.Character
تم تحديث الطبقات الداخلية لتشمل كتل يونيكود جديدة. أرغب دائمًا في رؤية ما وجده الأشخاص لإضافته إلى Unicode ، فيما يلي بعض الأمثلة:
- رموز الشطرنج
- أرقام المايا
- اللغة السغدية هي لغة إيرانية شرقية لم تعد تستخدم في القرن الحادي عشر.
- Sogdian القديم هو إصدار أقدم (وأظن أنه أكثر محدودية) من Sogdian
java.lang.Class
arrayType()
بإرجاع Class
لنوع الصفيف الذي تم وصف نوع المكون الخاص به بواسطة هذه Class
. يمكن التحقق من ذلك باستخدام jshell
:
jshell> (new String[2]).getClass().getName() $11 ==> "[Ljava.lang.String;" jshell> (new String[2]).getClass().arrayType() $12 ==> class [[Ljava.lang.String; jshell> "foo".getClass().arrayType() $15 ==> class [Ljava.lang.String;
لست متأكدًا تمامًا من معنى هذه الطريقة ، حيث أن كل ما تفعله هو إضافة Class
إلى النوع الذي تمثله هذه الفئة.
componentType()
، مثل getComponentType()
. يطرح السؤال - لماذا تضيف طريقة زائدة عن الحاجة؟
descriptorString()
- مرة أخرى ، تُرجع نفس النتيجة كـ getName()
. ومع ذلك ، من الضروري أن تقوم Class
الآن بتنفيذ واجهة TypeDescriptor
المرتبطة بواجهة برمجة تطبيقات JVM الثابتة الجديدة.
lava.lang.String
indent()
- يضيف سلسلة من المسافات indent()
إلى سلسلة. إذا كانت المعلمة سالبة ، فسيتم إزالة هذا العدد من المسافات البادئة (إن أمكن).
transform()
- يطبق الوظيفة المتوفرة على سلسلة. قد لا تكون النتيجة سلسلة.
java.lang.invoke
VarHandle
الآن toString()
لإرجاع وصف مضغوط.
java.net.SecureCacheResponse
و java.net.ssl.HttpsConnection
لديهم طريقة جديدة ، getSSLSession()
والتي ترجع Optional
يحتوي على SSLSession
المستخدمة في الاتصال.
java.nio.files
تحتوي فئة Files
على طريقة جديدة ، mismatch()
، والتي تقوم بإيجاد وإرجاع موضع بايت عدم التطابق الأول في محتويات ملفين ، أو -1L إذا لم يكن هناك عدم تطابق.
java.text
هناك فئة جديدة CompactNumberFormat
. هذا هو فئة فرعية من NumberFormat
رقم عشري في نموذج مضغوط. مثال على نموذج مضغوط - 1M
بدلاً من 1000000
، وبالتالي - يتطلب حرفين بدلاً من تسعة أحرف. تم توسيع NumberFormat
و getCompactNumberInstance()
طريقة getCompactNumberInstance()
الجديدة. يوجد أيضًا تعداد جديد ، NumberFormatStyle
والذي له معنيان: LONG و SHORT.
java.util.concurrent
يتضمن CompleteStage الآن العديد من النماذج المحملة بثلاث طرق:
- exceptionallyAsync
- exceptionallyCompose
- exceptionallyComposeAsync
تعمل هذه الطرق على توسيع إمكانيات إنشاء " CompletionStage
جديد من "موجود" ، " CompletionStage
إذا انتهى الحال مع استثناء. تحقق من وثائق API للحصول على التفاصيل.
javax.crypto
تحتوي فئة Cipher
على طريقة toString()
جديدة تقوم بإرجاع سلسلة تحتوي على تحويل Cipher
والوضع والموفر.
javax.naming.ldap.spi
هذه حزمة جديدة في JDK 12 وهي تحتوي على فئتين: LdapDnsProvider
، وهي فئة الموفرين لعمليات البحث عن DNS أثناء عمليات LDAP ، و LdapDnsProviderResults
التي تغلف نتيجة بحث DNS لعنوان URL LDAP.
أرجوحة
لا يزال يجري تحديث سوينغ! نعم ، getChooserShortcutPanelFiles()
لديه الآن طريقة getChooserShortcutPanelFiles()
جديدة. تقوم بإرجاع مجموعة من الملفات التي تمثل القيم المراد عرضها افتراضيًا في شريط اختصار تحديد الملف.
التغييرات JVM
JEP 189: شيناندواه : جامع البيانات المهملة في وقت منخفض
شيناندواه هو مشروع بحثي أعلنته ريد هات في عام 2014 ويركز على متطلبات تطبيق الكمون المنخفض لإدارة الذاكرة في JVM. أهدافها هي وقت توقف مؤقت أقصى يبلغ 1..10 مللي ثانية كومة الذاكرة المؤقتة لأكثر من 20 جيجابايت ( وبالتالي فهو غير مخصص للتطبيقات الصغيرة - كما أجاب أحد مطوري Shenandoah ، هذا ليس كذلك ويقوم بعمل ممتاز مع التطبيقات الصغيرة). تم تصميم أداة التجميع هذه لتعمل بالتوازي مع مؤشرات ترابط التطبيق ، لذا تجنب المشكلات التي نراها في معظم أدوات تجميع البيانات المهملة.
يهدف هذا التغيير إلى تحسين سلوك جامع G1 عندما يصل إلى هدف التأخير المحدد. يقسم G1 مساحة الكومة (القديمة والقديمة) إلى مناطق. والفكرة هي أنه في الجيل القديم لا تحتاج إلى جمع القمامة في عملية واحدة. عندما تحتاج G1 إلى جمع البيانات المهملة ، فإنها تحدد المناطق التي تحددها. وهذا ما يسمى مجموعة جمع. قبل إصدار JDK 12 ، عندما بدأ العمل في المجموعة ، كان لابد من إكمال كل العمل ، في جوهره ، كعملية ذرية. كانت المشكلة أنه في بعض الأحيان بسبب التغييرات في استخدام التطبيق لمساحة الكومة ، اتضح أن مجموعة المجموعة كانت كبيرة للغاية واستغرقت وقتًا طويلاً في التجميع ، مما أدى إلى حقيقة أنه لم يتم الوصول إلى وقت الإيقاف المؤقت.
في JDK 12 ، إذا حددت G1 هذا الموقف ، فسوف يقطع جمع البيانات في منتصف الطريق إذا لم يؤثر ذلك على قدرة التطبيق على مواصلة تخصيص مساحة للكائنات الجديدة. سيكون التأثير الصافي لـ G1 أفضل عند الوصول إلى وقت توقف مؤقت قصير.
هذا تحسين آخر للأداء في G1 ، لكن هناك تحسينًا آخر يتعلق بكيفية تفاعل JVM مع بقية النظام. من الواضح أن الذاكرة مطلوبة لكومة الذاكرة المؤقتة لـ JVM ، وعند بدء التشغيل ، تطلب الذاكرة من مُخصص الذاكرة الظاهرية لنظام التشغيل. عند بدء تشغيل التطبيق ، قد تكون هناك أوقات ينخفض فيها مقدار الذاكرة المطلوبة للكومة ، ويمكن إرجاع جزء من الذاكرة المخصصة إلى نظام التشغيل لاستخدامها من قبل التطبيقات الأخرى.
يقوم G1 بهذا بالفعل ، ولكن لا يمكنه القيام بذلك إلا في أحد المكانين. أولا ، خلال مجموعة كاملة ، وثانيا ، خلال واحدة من الدورات الموازية. يحاول G1 عدم القيام بمجموعة كاملة ، ومع انخفاض استخدام الذاكرة ، قد تكون هناك فترات مهمة بين دورات المجموعة. هذا يؤدي إلى حقيقة أن G1 يمكنه الاحتفاظ بذاكرة ثابتة لفترة طويلة.
في JDK 12 ، ستحاول G1 بشكل دوري متابعة أو تشغيل حلقة متوازية بينما يكون التطبيق في وضع الخمول لتحديد الاستخدام الكلي لكومة Java. يمكن إرجاع الذاكرة غير المستخدمة إلى نظام التشغيل في الوقت المناسب وبطريقة يمكن التنبؤ بها.
علامة سطر الأوامر الجديدة -XX:G1PeriodicGCInterval
يمكن استخدامها لتعيين عدد المللي ثانية بين الشيكات.
ستؤدي هذه الميزة إلى استخدام أكثر تحفظًا لذاكرة JVM للتطبيقات التي ظلت خاملاً لفترات طويلة من الوقت.
ميزات JDK جديدة أخرى
تم تطوير Java Microbenchmarking Harness (JMH) بواسطة Alexey Shipilev عندما كان يعمل في Oracle ويوفر منصة شاملة لتطوير اختبارات الأداء لتطبيقات Java. قام Alexey بعمل رائع لمساعدة الناس على تجنب العديد من الأخطاء البسيطة التي يرتكبونها عند محاولة تحليل أداء التطبيق: الاحماء ، تجنب الاستثناءات ، إلخ.
يمكن الآن تضمين JMH في OpenJDK. يمكن لأي شخص مهتم بالعمل على JDK نفسه وتغيير الرمز استخدام هذا لمقارنة الأداء قبل وبعد التغييرات ، وكذلك لمقارنة الأداء في الإصدارات المختلفة. يتم تضمين عدد من الاختبارات لتمكين الاختبار ؛ تصميم JMH هو أنه من السهل إضافة اختبارات جديدة إلى حيث هناك حاجة إليها.
يحتوي OpenJDK على منفذين لهندسة Arm64 ، أحدهما يوفره Oracle والآخر بواسطة Red Hat. نظرًا لأن هذا لم يكن ضروريًا ، وتوقف Oracle عن دعم Arm لثنائيات JDK الخاصة به ، فقد تقرر استخدام منفذ Red Hat فقط ، الذي لا يزال مدعومًا ومطورًا.
تستخدم فئة مشاركة البيانات (CDS) لتكون ميزة تجارية في Oracle JDK. مع انتقال حديث تم إجراؤه في JDK 11 لإزالة جميع الاختلافات الوظيفية بين Oracle JDK و OpenJDK ، تم تضمينه في OpenJDK.
لاستخدام CDS ، تحتاج إلى أرشيف تم إنشاؤه للفئات التي يتم تحميلها عند بدء تشغيل التطبيق. يحتوي JDK 12 للأنظمة الأساسية 64 بت الآن على ملف classes.jsa
في دليل lib/server
. هذا هو أرشيف CDS لـ "الفئات الافتراضية". أفترض أن هذا يعني جميع الفئات العامة في وحدات JDK ؛ لم أتمكن من إيجاد طريقة لفك ضغطها للتحقق. منذ تمكين CDS افتراضيًا ، وهو ما يعادل الخيار -Xshare:auto
في سطر الأوامر ، سيستفيد المستخدمون من أوقات بدء تشغيل التطبيق المحسنة منه.
النتائج
يوفر JDK 12 عددًا صغيرًا من الوظائف وواجهات برمجة التطبيقات ، مع switch
الأكثر إثارة للاهتمام للمطورين. بالتأكيد سيقدر مستخدمو G1 تحسينات الأداء.
مع الإصدار الجديد من الإصدار ، أنصح جميع المستخدمين باختبار تطبيقاتهم في هذا الإصدار. سيساعدك تتبع التغييرات الإضافية في تجنب المفاجآت إذا قررت الانتقال إلى الإصدار التالي من الدعم طويل الأجل.
لدينا إصدارات JDK 12 مجانية لـ Zulu Community Edition لمساعدتك في الاختبار. تأكد من تجربتها.