۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

پروژه والهالا: نگاهی به بازسازی حماسی جاوا

Valhalla چیزی کمتر از یک بازنگری زبان جاوا نیست، که نویدبخش اصلاح مشکلات عملکرد طولانی مدت است. در اینجا اولین نگاهی به آنچه در راه است، با کلاس های ارزش جدید و کلاس های ابتدایی است.

Valhalla چیزی کمتر از یک بازنگری زبان جاوا نیست، که نویدبخش اصلاح مشکلات عملکرد طولانی مدت است. در اینجا اولین نگاهی به آنچه در راه است، با کلاس های ارزش جدید و کلاس های ابتدایی است.

همه چیز در جاوا یک شی است، به جز موارد اولیه مانند int. به نظر می رسد که اخطار کوچک پیامدهای بزرگی برای زبان دارد که در طول سال ها ترکیب شده است. این تصمیم طراحی به ظاهر جزئی باعث ایجاد مشکلاتی در زمینه های کلیدی مانند مجموعه ها و ژنریک می شود. همچنین بهینه سازی عملکرد خاصی را محدود می کند. پروژه Valhalla، بازساز زبان جاوا، با هدف اصلاح این مشکلات است. برایان گوتز، سرپرست پروژه والهالا گفته است که والهالا “درمان خواهد کرد. شکاف بین چیزهای اولیه و اشیاء.”

منصفانه است که بگوییم Project Valhalla یک بازسازی حماسی است که به دنبال پرداختن به بدهی های فنی است که از زمان شروع جاوا در اعماق پلت فرم مدفون شده است. این تکامل کامل ثابت می کند که جاوا نه تنها کلاسیک است، بلکه در خط مقدم طراحی زبان برنامه نویسی باقی می ماند. بیایید نگاهی به اجزای فنی کلیدی پروژه والهالا بیندازیم و چرا آنها برای آینده جاوا بسیار حیاتی هستند.

اصول و عملکرد جاوا

زمانی که جاوا برای اولین بار در دهه ۱۹۹۰ معرفی شد، تصمیم گرفته شد که همه انواع ایجاد شده توسط کاربر کلاس باشند. فقط تعداد انگشت شماری از انواع اولیه به عنوان خاص کنار گذاشته شدند. اینها به عنوان ساختارهای کلاس مبتنی بر اشاره گر استفاده نمی شدند، بلکه مستقیماً به انواع سیستم عامل نگاشت می شدند. هشت نوع اولیه عبارتند از: int، byte، short، long، float، دوگانه، Boolean و char.

نگاشت مستقیم این متغیرها به سیستم عامل برای عملکرد بهتر بود زیرا عملیات عددی در هنگام واگذاری سربار ارجاعی اشیا بهتر عمل کرد. علاوه بر این، تمام داده ها در نهایت به این هشت نوع اولیه در یک برنامه حل می شوند. کلاس‌ها فقط نوعی لایه ساختاری و سازمانی هستند که راه‌های قدرتمندتری برای گروه‌بندی و مدیریت انواع اولیه ارائه می‌دهد. تنها نوع دیگر ساختار آرایه است. ابتدایی ها، کلاس ها و آرایه ها طیف وسیعی از قدرت بیان جاوا را تشکیل می دهند. و قدرتمند است.

اما اولیه‌ها نوع متفاوتی از حیوانات نسبت به کلاس‌ها و آرایه‌ها هستند. به عنوان برنامه نویس، ما یاد گرفته ایم که به طور مستقیم با تفاوت ها کنار بیاییم. برای مثال، مقادیر اولیه ارزش عبوری هستند در حالی که اشیاء مرجع عبوری هستند. چرا این بسیار عمیق است. به مسئله هویت برمی گردد. می توان گفت که مقادیر اولیه قابل تعویض هستند: int x = 4 عدد صحیح ۴ است، مهم نیست کجا ظاهر می شود. هر نمونه ای از عدد صحیح ۴ به خوبی نمونه دیگر است. ما این تمایز را در equals() در مقابل == می‌بینیم، جایی که اولی معادل ارزش اشیا را آزمایش می‌کند و دومی هویت را آزمایش می‌کند. اگر دو مرجع فضای یکسانی در حافظه داشته باشند، == را برآورده می کنند، به این معنی که آنها یک شی هستند. هر int تنظیم شده روی ۴ نیز == را برآورده می کند، در حالی که int به هیچ وجه .equals() را پشتیبانی نمی کند.

به نوعی، می‌توان گفت که برای یک اولیه، مقدار هویت است. این مفهوم ساده منبع عملکرد متغیر اولیه است. پلتفرم تا دستورالعمل‌های CPU می‌تواند با خیال راحت فرض کند که هر int 4 به اندازه دیگری خوب است و آزادانه آنها را در حافظه پنهان و کپی می‌کند.

اشیاء مرجع و حافظه

ماشین مجازی جاوا (JVM) می‌تواند از روشی استفاده کند که ابتدا با آنها کار می‌کند تا نحوه ذخیره، بازیابی و عملکرد آنها را بهینه کند. به طور خاص، اگر پلتفرم تشخیص دهد که متغیری تغییر نکرده است (یعنی ثابت یا غیرقابل تغییر است)، آنگاه برای بهینه سازی در دسترس است.

در مقابل، اشیا در برابر این نوع بهینه سازی مقاوم هستند زیرا هویتی متمایز از ارزش خود دارند. این چیزی است که .equals() برای آن آزمایش می کند: مکان شی در حافظه. این پلتفرم و ماشین را به دنیای مراجع منتقل می‌کند، جایی که فقط ارجاع به نمونه واقعی انجام می‌شود.

به عنوان نمونه ای از یک کلاس، یک شی داده هایی را در خود نگه می دارد که هم می توانند ابتدایی باشند و هم کلاس ها و آرایه های دیگر. خود شی با یک دسته اشاره گر خطاب می شود. این یک شبکه از مراجع ایجاد می کند: گراف شی. هر زمان که مقداری تغییر کرد – یا حتی اگر ممکن است تغییر کند، JVM مجبور است یک رکورد قطعی از شی را برای ارجاع نگه دارد. نیاز به ارجاع به اشیا مانعی برای بهینه سازی عملکرد است.

مشکلات عملکرد به همین جا ختم نمی شود. ماهیت اشیاء به عنوان سطل ارجاع به این معنی است که آنها به روشی بسیار نرم در حافظه وجود دارند. Fluffy اصطلاح فنی من برای توصیف این واقعیت است که JVM نمی تواند اشیا را فشرده کند تا ردپای حافظه آنها را به حداقل برساند. هنگامی که یک شی به عنوان بخشی از ساختار خود به یک شیء دیگر اشاره دارد، JVM مجبور است آن رابطه اشاره گر را حفظ کند. (در برخی موارد، یک بهینه‌سازی هوشمندانه می‌تواند به تعیین اینکه یک مرجع تودرتو تنها دسته یک موجودیت خاص است کمک کند.)

در وبلاگ ایالت والهالا پس از آن، گوتز از آرایه ای از نقاط برای نشان دادن ماهیت غیر متراکم مراجع استفاده می کند. می توانیم از کلاس استفاده کنیم. برای مثال، فرض کنید یک کلاس Landmark با نام و یک فیلد موقعیت جغرافیایی داریم. اینها به ساختار حافظه ای مانند آنچه در اینجا نشان داده شده دلالت دارند:

نمودار حافظه شی.

شکل ۱. ردپای حافظه “کرکی” از اشیاء جاوا.

آنچه که می‌خواهیم به آن برسیم، توانایی نگه داشتن یک شی است، در صورت لزوم، همانطور که در شکل ۲ نشان داده شده است.

Project Valhalla--یک شی متراکم در حافظه

شکل ۲. یک شی متراکم در حافظه.

نقاط درد عملکرد جاوا

این یک نمای کلی از چالش های عملکردی است که با تصمیمات اولیه طراحی در پلتفرم جاوا ایجاد شده است. حال بیایید در نظر بگیریم که این تصمیمات چگونه بر عملکرد در سه حوزه کلیدی تأثیر می‌گذارند:

  • فراخوانی روش و مقدار عبور
  • جعبه‌ها و اتوباکسینگ
  • عمومی و جریان

فراخوانی روش و مقدار عبور

ساختار پیش‌فرض اشیاء در حافظه هم برای حافظه و هم برای کش ناکارآمد است. علاوه بر این، فرصتی برای دستیابی به سود در روش فراخوانی قراردادها وجود دارد. توانایی ارسال آرگومان‌های فراخوانی به ارزش به روش‌ها با نحو کلاس (در صورت لزوم) مزایای عملکردی جدی را به همراه خواهد داشت.

جعبه‌ها و اتوباکسینگ

فرای ناکارآمدی‌ها، تمایز بین کلاس‌های اولیه و کلاس‌ها مشکلاتی را در سطح زبان ایجاد می‌کند. ایجاد «جعبه‌های» ابتدایی مانند Integer و Long (همراه با جعبه‌سازی خودکار) تلاشی برای کاهش مشکلات ناشی از این تمایز است. با این حال، واقعاً آنها را برطرف نمی کند، و درجه ای از سربار را هم برای توسعه دهنده و هم برای دستگاه معرفی می کند. به عنوان یک توسعه دهنده، باید تفاوت بین int و Integer را به خاطر بسپارید. (به ArrayList، int[]، Integer[] و فقدان ArrayList اشاره نکنیم.) در همین حال، ماشین باید بین این دو تبدیل کند.

به نوعی، بوکس بدترین هر دو دنیا را به ما می دهد. پنهان کردن ظرایف زیربنایی نحوه کار این موجودیت ها، دسترسی به قدرت نحو کلاس و عملکرد اولیه را دشوارتر می کند.

عمومی و جریان‌ها

همه این ملاحظات در ژنریک به نتیجه می رسند. ژنریک ها به این منظور طراحی شده اند که تعمیم عملکردها را آسان تر و واضح تر کنند. اما حضور پرسنیک این مجموعه از متغیرهای غیر شی (اولیه ها) باعث از بین رفتن آن می شود. وجود ندارد — نمی تواند وجود داشته باشد زیرا int اصلاً یک کلاس نیست. از Object فرود نمی آید. اجازه چند شکلی را نمی دهد.

این مشکل سپس در کتابخانه‌هایی مانند مجموعه‌ها و جریان‌های جاوا آشکار می‌شود، جایی که ایده آل توابع کتابخانه عمومی مجبور است با واقعیت int در مقابل Integer، long در مقابل Long و به زودی. در حال حاضر، راه‌حل ارائه IntStream و سایر تغییرات غیرعمومی است.

راه‌حل Valhalla: کلاس‌های ارزش و کلاس‌های اولیه

پروژه Valhalla به این سه نقطه درد عملکرد جاوا در ریشه حمله می کند. اولین و اساسی ترین مفهوم کلاس ارزش است. ایده اینجا این است که می‌توانید کلاسی را تعریف کنید که از همه چیزهایی که در مورد کلاس‌ها عالی هستند، مانند داشتن متدها و توانایی انجام کلیات، اما بدون هویت، مشارکت می‌کند. در عمل، این بدان معناست که کلاس‌ها تغییرناپذیر هستند و نمی‌توانند طرح‌بندی-چند شکلی باشند (که در آن سوپرکلاس می‌تواند از طریق ویژگی‌های انتزاعی بر روی زیر کلاس‌ها عمل کند). این مانند کلاسی است که فقط یک سطل از چیزهای اولیه است.

کلاس‌های ارزش راهی واضح و قطعی برای به دست آوردن ویژگی‌های عملکردی که به دنبال آن هستیم در حالی که هنوز به مزایای نحو و رفتار کلاس دسترسی داریم، به ما می‌دهند. این بدان معناست که سازندگان کتابخانه همچنین می توانند از آنها برای بهبود طراحی API خود استفاده کنند.

یک قدم جلوتر، کلاس ابتدایی است که مانند یک کلاس ارزش شدیدتر است. در اصل، کلاس اولیه یک پوشش نازک در اطراف یک متغیر اولیه واقعی است، اما با متدهای کلاس. این چیزی شبیه جعبه های سفارشی و ساده اولیه است. پیشرفت در این است که سیستم بوکس را واضح تر و قابل گسترش تر می کند. علاوه بر این، ارزش اولیه پیچیده شده توسط یک کلاس ابتدایی، ویژگی های عملکردی اولیه را حفظ می کند (بکس زیر هود و جعبه گشایی وجود ندارد). بنابراین، کلاس ابتدایی را می‌توان در هر جایی که کلاس‌ها می‌توان استفاده کرد—مثلاً در یک آرایه Object[]. انواع اولیه تهی نخواهند بود (آنها را نمی توان روی null تنظیم کرد).

به طور کلی، می‌توان گفت که Project Valhalla انواع اولیه و تعریف‌شده توسط کاربر را به هم نزدیک‌تر می‌کند. این به توسعه دهندگان گزینه های بیشتری در طیف بین اولیه و اشیاء خالص می دهد و مبادلات را واضح می کند. همچنین این عملیات را به طور کلی سازگارتر می کند. به طور خاص، سیستم بدوی جدید نحوه کارکردن اشیاء و بدوی، نحوه جعبه‌بندی آنها و نحوه افزودن موارد جدید را هموار می‌کند.

مقدار جدید و کلیدواژه های ابتدایی

والهالا چند پیشنهاد نحوی مختلف دیده است، اما اکنون پروژه شکل و جهت مشخصی دارد. دو کلمه کلیدی جدید کلیدواژه کلاس را تغییر می دهند: value و primitive. کلاسی که با نحو کلاس ارزش اعلام شده است هویت خود را تسلیم می کند اما بهبود عملکرد را به دست می آورد. علاوه بر محدودیت‌های تغییرپذیری و چندشکلی، بسیاری از چیزهایی که از یک کلاس انتظار دارید همچنان اعمال می‌شوند، و چنین کلاس‌هایی می‌توانند به طور کامل در کدهای عمومی شرکت کنند (مانند object[] یا ArrayList ). کلاس های مقدار به طور پیش فرض null هستند.

نحو کلاس primitive کلاسی را ایجاد می کند که یک قدم بیشتر از اشیاء سنتی و به سمت اولیه های سنتی فاصله دارد. این کلاس‌ها به‌طور پیش‌فرض مقدار زیرین فیلدها (۰ برای int، ۰.۰ برای double و غیره) را پیش‌فرض می‌کنند و نمی‌توانند null باشند. کلاس های اولیه بیشترین سود را در بهینه سازی دارند و بیشترین قربانی را از نظر ویژگی ها می کنند. کلاس های اولیه ۳۲ بیت ایمن نیستند الف>. کلاس ابتدایی در نهایت برای مدل‌سازی همه موارد اولیه در پلتفرم استفاده می‌شود، به این معنی که افزوده‌های اولیه تعریف‌شده توسط کاربر و کتابخانه در سیستمی مشابه با داخلی‌ها شرکت خواهند کرد.

IdentityObject و ValueObject

پروژه Valhalla همچنین دو رابط جدید را معرفی می کند: IdentityObject و ValueObject. این موارد به تعیین زمان اجرا اجازه می دهد که با چه نوع کلاسی سروکار دارید.

شاید اساسی ترین تغییر نحو برای توسعه دهندگان باتجربه جاوا اضافه کردن عضو .ref باشد. همه انواع اکنون دارای فیلد V.ref() خواهند بود. این فیلد مانند کادر در موارد اولیه عمل می‌کند، بنابراین int.ref مشابه قرار دادن یک int با یک Integer است. کلاس های عادی .ref را به مرجع خود حل می کنند. تأثیر کلی این است که روشی ثابت برای درخواست مرجع در مورد یک متغیر صرف نظر از نوع آن وجود داشته باشد. این همچنین باعث می‌شود که همه آرایه‌های جاوا «کوواریانت» شوند، یعنی همگی از Object[] فرود می‌آیند. بنابراین، int[] اکنون از Object[] فرود می‌آید و می‌تواند در هر کجا که فراخوانی شود استفاده شود.

نتیجه گیری

کلاس‌های ارزش و کلاس‌های اولیه تأثیر زیادی بر جاوا و اکوسیستم آن خواهند داشت. نقشه راه فعلی پروژه والهالا در نظر دارد ابتدا طبقات ارزشی را معرفی کند و سپس طبقات اولیه را معرفی کند. . بعدی مهاجرت کلاس های بوکس اولیه موجود (مانند Integer) برای استفاده از کلاس اولیه جدید خواهد بود. با در دست داشتن این ویژگی‌ها، ویژگی بعدی، به نام جهانی ژنریک، به کلاس‌های اولیه اجازه می‌دهد تا مستقیماً با ژنریک‌ها استفاده شوند و بسیاری از پیچیدگی‌های استفاده مجدد در APIها را هموار کند. در نهایت، ژنریک های تخصصی (که تمام قابلیت های بیانی T را گسترش می دهد Foo اجازه می دهد) با کلاس های اولیه ادغام می شود.

پروژه Valhalla و پروژه هایی که شامل آن می شود هنوز در مراحل طراحی هستند، اما ما در حال نزدیکتر شدن هستیم. فعالیت فعلی نشان می‌دهد که طولی نمی‌کشد تا کلاس‌های ارزش در پیش‌نمایش JDK افت کنند.

فرای همه کارهای فنی جالب، حس سرزندگی مداوم جاوا است. اینکه هم اراده و هم توانایی برای انجام فرآیند شناسایی جایی که پلتفرم می تواند به روش های اساسی تکامل یابد وجود دارد، گواه تعهد واقعی به حفظ جاوا مرتبط است. Project Loom تعهد دیگری است که به دیدگاه خوشبینانه از آینده جاوا اهمیت می دهد. یکی از ویژگی‌های ضروری آن پروژه رشته‌های مجازی است که جایگزینی کارآمد برای رشته‌های جاوا سنتی است.

در سطح طراحی زبان، Valhalla یکی از جالب‌ترین چیزهایی است که در هر کجا اتفاق می‌افتد. با پایان یافتن سال ۲۰۲۳، بسیاری از توسعه دهندگان آینده Valhalla را پیش بینی می کنند. علاقه مندان به جاوا در من نمی توانند منتظر دیدن نحو نهایی شوند. در عین حال، امیدوارم که پیچیدگی زبان را بیش از حد افزایش ندهد، به خصوص برای مبتدیان.