قسمت دوم Java Zone (ماشین مجازی جاوا)

 ساختار JVM
ساختار JVM

خب همونطور که در قسمت قبلی گفتم، امروز قراره JVM رو بررسی کنیم.

ماشین مجازی جاوا دو وظیفه اصلی داره:

  • اجرای برنامه های جاوایی روی هر محیطی که JVM وجود داشته باشه.
  • مدیریت و بهینه سازی حافظه برنامه.
"خوبه بدونید متد main ای که نقطه آغاز برنامه های جاوایی است، توسط همین JVM صدا زده میشه و بقیه ماجرا..."

سوال! ماشین مجازی جاوا رو چه کسانی توسعه دادن؟

رسمی ترین پیاده سازی JVM توسط پروژه متن-باز OpenJDK که نتیجه زحمات شرکت Sun Microsystems در سال 2006 بود، صورت گرفته.



خب بریم سراغ ساختار JVM

قسمت Class Loader

بارگذاری فایل کلاس ها به عهده این بخشه. این قسمت مسئول اجرای 3 عملیات است:

  • بارگذاری
  • اتصال
  • مقداردهی

بارگذاری:

اینجا Class Loader فایل های class. رو میخونه و داده های باینری مربوطه رو در method area ذخیره میکنه. بعد از بارگذاری فایل های class.، ماشین مجازی شروع به ساختن آبجکتی از نوعی کلاس برای مموری هیپ میکنه. توجه داشته باشین که این آبجکت از نوع کلاسی هست که در java.lang package تعریف شده. برنامه نویس از این کلاس میتونه برای گرفتن اطلاعات مربوط به کلاس استفاده بکنه. اطلاعاتی مثل نام کلاس، نام کلاس والد، نام متدها و متغیرها و غیره.

به عنوان مثال برای گرفتن چنین اطلاعاتی اینطوری عمل میکنیم.

https://gist.github.com/faramarzaf/b0d913ab80d8348badcf538267c296c6


اتصال:

محل انجام عملیات بررسی و آماده سازی.

  • بررسی: در این مرحله درستی فرمت فایل های class. بررسی و چک میشه که توسط کامپایلر معتبر تولید شده یا نه. اگر اعتبار سنجی با شکست مواجه بشه به اکسپشن java.lang.VerifyError برمیخوریم.
  • آماده سازی: JVM اقدام به اختصاص دادن حافظه برای متغیرهای کلاس و مقدار دهی پیشفرض به متغیرها میکنه.

مقدار دهی:

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



انواع Class loader
انواع Class loader

به طور کلی 3 نوع class loader وجود داره

  • نوع Bootstrap: هر ماشین مجازی جاوایی باید این نوع از class loader رو داشته باشه. کار اش بارگذاری کلاس های java API موجود در مسیر JAVA_HOME/jre/lib هست که این مسیر به bootstrap path معروفه و با زبان های C و C++ پیاده سازی شدن.
  • نوع Extension: به نوعی فرزند نوع Bootstrap به حساب میاد. مسئول بارگذاری کلاس های موجود در مسیر JAVA_HOME/jre/lib/ext هست و در جاوا توسط کلاس sun.misc.Launcher$ExtClassLoade پیاده سازی شده.

نوع System/Application: این نوع هم فرزند نوع Extension هست و مسئول بارگذاری کلاس ها در مسیر برنامه اصلی هست و در جاوا توسط کلاس sun.misc.Launcher$AppClassLoader پیاده سازی شده.

https://gist.github.com/faramarzaf/a565890abe12a5121902d2ad5a31a4da


کلاس String توسط bootstrap بارگذاری شده و چون از نوع آبجکت جاوا نیست، مقدار null برمیگردونه

در اینجا کلاس Test توسط نوع Application بارگذاری شده.



توجه داشته باشین که JVM از اصل Delegation-Hierarchy برای بارگذاری کلاس ها پیروی میکنه. به این صورت که System class loader ها درخواست رو به extension classها و extension class ها همین درخواست رو به boot-strap class loader محول میکنن. اگر کلاس در مسیر boot-strap پیدا شد، کلاس بارگذاری میشه. در نهایت اگر system class loader ها در بارگذاری کلاس ها با شکست مواجه بشن، به اکسپشن java.lang.ClassNotFoundException برمیخوریم.


خب بریم به قسمت دوم یعنی حافظه JVM

  • قسمت Method area: در این قسمت همه اطلاعات در سطح کلاس مثل اسم کلاس، اسم والد کلاس (از لحاظ سلسه مراتب اولین کلاس والد از پایین)، متدها و اطلاعات متغیرهای استاتیک وغیراستاتیک و ... ذخیره شدن. در ضمن به ازای هر JVM یک (Method area) وجود داره و در سرتاسر JVM یک منبع مشترکه.
  • قسمت Heap area: تمام اطلاعات مربوط به آبجکت ها در حافظه هیپ ذخیره میشن و مثل Method area به ازای هر JVM یک حافظه هیپ وجود داره.
  • قسمت Stack area: برای هر ترد (thread)، JVM یک Stack از نوع اجرا در لحظه (run-time) میسازه که در همینجا ذخیره میشه. در ضمن تمام متغیرهای در سطح متدها هم اینجا ذخیره میشن. بعد از اتمام حیات ترد این استک توسط JVM از بین میره.
  • قسمت PC Registers: محل ذخیره سازی آدرس دستور اجرای ترد کنونی هست. مشخصه که هر ترد PC Registers متفاوتی داره.
  • قسمت Native method stacks: برای هر ترد یک native stack جداگانه ساخته میشه. اینجا محل نگهداری کدهای نیتیو بسته به نوع کتابخانه نیتیو است که به زبانی به غیر از جاوا نوشته شده.

میرسیم به بخش اجرایی JVM

در این قسمت نوبت میرسه به اجرای فایل های class. یا همون بایت کد هامون. خط به خط بایت کد ها اینجا خونده میشن و بسته به دستورات داخل اش، اجرا رو شروع میکنن که تو این قسمت با 3 بخش مهم مواجهیم.

  • قسمت تفسیر: خط به خط بایت کدها تفسیر میشن و سپس اجرا میشن. البته ضعفی که این قسمت داره اینه که در مواجهه با کدهای تکراری، هربار عمل تفسیر اجرا میشه و این خوب نیست.
  • قسمت Just-In-Time Compiler: توضیحات این بخش رو توی سری قبلی نوشتم ولی بدونید که اینجا JIT باعث افزایش راندمان عمل تفسیر میشه.
  • قسمت Garbage Collector: تمام آبجکت هایی که بدون ارجاع موندن رو از حافظه پاک میکنه.

آخرین بخش Java Native Interface (JNI)

واسطی است که با کدهای کتابخونه های نیتیو در ارتباطه و کتابخونه هایی که برای اجرای برنامه لازم هستن رو مهیا میکنه (که با C, C++ نوشته شدن) و به عنوان مثال JVM رو وادار میکنه در بخش هایی که ممکنه به سخت افزار نیاز یا تعاملی با سیستم عامل داشته باشه، از این کتابخونه ها استفاده بکنه.


خب امیدوارم این بخش هم براتون مفید بوده باشه.

خوب و خوش باشید ??