معماری ماشین مجازی جاوا

تمامی توسعه دهندگان جاوا می دانند که بایتکد با استفاده از JRE اجرا می شود. ولی بیشتر آنها نمی دانند که JRE پیادهسازی JVM میباشد، که بایتکد را تحلیل، کد را تفسیر و اجرا می نماید. به عنوان یک برنامه نویس جاوا میبایستی معماری JVM را بدانیم تا کدهایی کارآمد را توسعه دهیم. در این مقاله، به طور عمیق معماری JVM در جاوا و اجزای مختلف آن را معرفی مینماییم.
ماشین مجازی جاوا چیست؟
ماشین مجازی پیادهسازی نرم افزاری از یک سختافزار میباشد. جاوا با هدف اولیه “یکبار بنویس همه جا استفاده کن” ارائه شد، که بر روی ماشین مجازی اجرا میشود. کامپایلر وظیفه کامپایل فایلهای .java به فایلهای .class را دارد. فایلهای .class وارد ماشین مجازی میشود، که وظیفه بارگذاری و اجرای کلاس فایل را بر عهده دارد. در ادامه معماری JVM را مشاهده مینمایید.
چگونه JVM کار میکند؟
همان طور که در شکل معماری بالا نمایش داده میشود، JVM به سه بخش اصلی تقسیم میشود.
- زیرسیستم بارگذاری کلاس
- ناحیه داده زمان اجرا
- موتور اجرا
- زیرسیستم بارگذاری کلاس
بارگذاری کلاس به صورت پویا توسط زیرسیستم بارگذاری کلاس مدیریت میشود. این بخش در زمان اجرا (نه در زمان کامپایل) برای اولین بار که به کلاس مراجعه می شود وظیفه مقداردهی اولیه، ارتباط دهی و بارگذاری را دارد.
1.1 بارگذاری
کلاسها با استفاده از این کامپوننت بارگذاری میشوند. بارگذار کلاس Boot Strap، بارگذار کلاس Extension و بارگذار کلاس Application، سه بارگذار اصلی در سیستم می باشند.
- بارگذار کلاس Boot Strap- این بارگذار دارای بالاترین اولویت می باشد و مسئولیت بارگذاری کلاسها از مسیر کلاس bootstrap را دارد.
- بارگذار کلاس Extension- مسئولیت بارگذاری کلاسهای موجود در شاخه ext را دارد.
- بارگذار کلاس Application- مسئولیت بارگذاری کلاسهای سطح Application (که در مسیر متغیر محیطی Environment Variable مشخص شده اند) را دارد.
بارگذار کلاس از الگوریتم Delegation Hierarchy جهت بارگذاری کلاسها استفاده مینماید.
1.2 ارتباط دهی
1. بررسی کردن – در این قسمت bytecode بررسی میشود در صورتی که مشکلی رخ دهد، خطای verification error مواجه خواهیم شد.
2. آماده سازی – برای تمامی متغیرها حافظه با مقادیر پیش فرض و اولیه مقداردهی خواهد شد.
3. حل کردن – تمامی ارجاعات حافظه اختصاری با ارجاعات اصلی از ناحیه متد جایگزین خواهد شد.
1.3 مقداردهی
در مرحله آخر بارگذاری کلاس، تمامی متغیرهای استاتیک با مقدار اصلی مقدار دهی میشوند و بلاک های استاتیک اجرا میشوند.
2. ناحیه زمان اجرا داده
این بخش به 5 قسمت تقسیم می شود:
- ناحیه متد – تمامی دادههای سطح کلاس و متغیرهای استاتیک در این قسمت ذخیره خواهند شد. به ازای JVM تنها یک ناحیه متد وجود دارد که به صورت اشتراکی میباشد.
- ناحیه Heap – تمامی اشیاء، متغیرها و آرایهها مرتبط به اشیاء در این بخش ذخیره خواهند شد. تنها یک ناحیه Heap به ازای هر JVM وجود دارد. از آنجایی که ناحیههای متد و Heap حافظه را بین چندین نخ به صورت اشتراکی تقسیم مینمایند، دادههای ذخیره شده بین آنها امن نخواهد بود.
- ناحیه Stack – برای هر Thread، یک stack در زمان اجرا به صورت جداگانه ایجاد خواهد شد. برای هر فراخوانی method، بخشی در ناحیه حافظه با نام قاب استک در نظر گرفته خواهد شد. تمامی متغیرهای محلی در حافظه Stack ایجاد میشود. ناحیه Stack، به دلیل عدم اشتراک منابع در نخها امن میباشد. قاب Stack به سه بخش تقسیم میشود:
- ناحیه متغیر محلی آرایه – مربوط به متغیرهای محلی ایجاد شده و مقادیر مرتبط آنها میباشد.
- پشته عملیاتی – در صورتی که نیاز به عملیات میانی باشد، این عملیات در این بخش از حافظه صورت میپذیرد.
- داده قاب – تمامی سمبول های مرتبط به متد در این بخش ذخیره میشود. در هر exception که رخ میدهد، هر یک از بلاکهای اطلاعاتی در قاب داده اصلاح میشود.
- رجیسترهای PC – هر نخ دارای رجیسترهای PC مجزایی خواهد بود، جهت نگهداری آدرس دستور اجرایی فعلی وقتی که دستور اجرا شود مقدار رجیستر PC با مقدار دستور بعدی به روز رسانی خواهی شد.
- استک های Method اصلی – استک Method اصلی اطلاعات متد اصلی را نگه میدارد. برای هر نخ، یک استک متد اصلی جداگانه ایجاد خواهد شد.
3. موتور اجرایی
بایت کد که در ناحیه داده زمان اجرا با استفاده از موتور اجرایی اجرا خواهد شد. موتور اجرایی بایت کد را میخواند و قسمت به قسمت آن را اجرا مینماید.
- مفسر – مفسر بایت کد را با سرعت بالا تفسیر مینماید، ولی آن را به کندی اجرا مینماید. یکی از معایب مفسر، در صورتی که متدی چندین بار فراخوانی شود، هر بار یک مفسر جدیدی نیاز میباشد.
- کامپایلر JIT – کامپایلر JIT معایب مفسر را کاهش میدهد. موتور اجرایی با استفاده از مفسر اقدام به تبدیل بایت کد مینماید ولی در صورتی که بخشی تکراری در کد وجود داشته باشد از کامپایلر JIT استفاده خواهد نمود، که تمام بایت کد را کامپایل میکند و به کد اصلی تبدیل مینماید. این کد اصلی به صورت مستقیم برای فراخوانیهای تکراری استفاده خواهد شد که باعث افزایش کارایی سیستم میشود.
- تولید کننده کد سطح میانی – کد سطح میانی تولید مینماید.
- بهینه ساز کد – مسئولیت تولید کد اصلی یا کد سطح ماشین را دارد.
- تاریخچه – بخشی ویژه که مسئولیت یافتن hotspot را دارد. (بدون توجه به تعداد فراخوانی متدها)
- جمع آوری کننده زبالهها – اشیا بدون ارجاع را جمع آوری مینماید. این سیستم با دستور “System.gc()” قابل فراخوانی میباشد، ولی به هیچ عنوان جمع آوری زبالهها را گارانتی نمیکند.
رابط کاربری اصلی جاوا: با کتابخانههای متد اصلی ارتباط برقرار مینماید و کتابخانههای اصلی جهت موتور اجرایی را فراهم میآورد.
منبع: