به نام خدا
تقریبا همه برنامه نویسان جاوا میدانند که Bytecode ها به وسیله (JRE (Java Runtime Environment اجرا میشوند. میتوان یکی از مهمترین اجزای JRE را ماشین مجازی جاوا یا همان Java Virtual Machine ) JVM ) در نظر گرفت که وظیفه آنالیز کردن و اجرا کردنBytecode ها را برعهده دارد، معمولا برنامه نویسان احتیاج ندارند که بدانند که JVM چگونه کار میکند. بنابراین برنامه ها و کتابخانه های زیادی نوشته شده است بدون اینکه برنامه نویسان آن اطلاعی عمیقی از JVM داشته باشند.
بهرحال اگر یک برنامه نویس جاوا ساختار JVM را بشناسد اساسا جاوا را بهتر شناخته است و بهتر میتواند مشکلات را حل کرده و تصمیم گیری کند.
من در این مقاله قصد دارم ساختار JVM و نحوه اجرا کردن Bytecode ها را توضیح بدهم، امیدوارم که مفید باشه.
میتوان اجزای تشکیل دهنده ی JRE را Java API و JVM در نظر گرفت که نقش JVM خواندن Bytecode های داخل کلاس ها (که نتیجه کامپایل شدن کد جاوا است) و از طریق Class Loader ها آنها را بارگذاری کردن در نتیجه اجرا کردن آنها بوسیله Java API است.
ماشین مجازی (Virtual Machine)
ماشین مجازی (VM) یک نرم افزار پیاده سازی شده از یک ماشین (مثلا یک رایانه) است که برنامه ها را مانند یک ماشین مجزا اجرا میکند، جاوا بر اساس ساختار ماشین مجازی طراحی شده است که بتواند ساختار WORA یعنی Write Once Run Anywhere را پیاده سازی کند، ساختار و معماری مهمی که معمولا نادیده گرفته میشود، بنابراین ماشین مجازی جاوا (JVM) میتواند کدی را که یکبار نوشته شده است و به Bytecode تبدیل شده است را بدون تغییر در هر ماشینی که JVM بر روی آن نصب میشود اجرا کند.
میتوان از موارد زیر به عنوان ویژگیهای JVM یاد کرد :
ماشین مجازی بر اساس ساختار استک : با توجه به برخی از معماری مشهور رایانه ای مانند Intel x86 و ARM که بر اساس ساختار داده ای رجستر میباشند. ولی همیشه JVM بر اساس ساختار داده استک اجرا میشود.
گربیج کالکشن (Garbage Collection) : ترجمه فارسی این کلمه را دوست ندارم برای همین معادل تلفظ انگلیسیشو به فارسی نوشتم، باتوجه به کد نوشته شده و کتابخانه های اضافه شده به برنامه بطور مداوم اشیا مختلف ساخته میشود که به وسیله GC پاک میشوند و از بین میروند.
تضمین کردن مستقل بودن از پلتفرم با تعریف داده ساختارهای اولیه (Primitive Data Type) : در بعضی از زبانهای برنامه نویسی مانند C باتوجه به پلتفرم در حال اجرا سایزهای مختلفی برای متغییر ها در نظر گرفته میشود. ولی JVM بطور واضح داده ساختار های اولیه یا Primitive Data Type ها را تعریف میکند که همیشه و در هر پلتفرمی سایز مشخصی را میگیرند. (مثلا همیشه int چهار بایت فضا میگیرد)
جاوا توسط شرکت سان مایکروسیستمز طراحی و پیاده سازی و توسعه پیدا کرده است (که این شرکت توسط شرکت اوراکل خریداری شد) و هر کسی میتواند با رعایت کردن استانداردهای ماشین مجازی جاوا آنرا پیاده سازی کند و ماشن مجازی جدیدی پیاده سازی کند همانطور که ماشین های مجازی زیادی وجود دارد که میتوان به Oracle HotSpot JVM و IBM J9 و Dalvik VM که توسط شرکت گوگل برای سیستم عامل اندروید استفاده میشود اشاره کرد، ولی برخلاف بقیه ی ماشین های مجازی جاوا که از ساختار استک استفاده میکنند Dalvik VM از ساختار رجیستر استفاده میکند.
بایت کد ها (Java Bytecode)
برای پیاده سازی WORA ، ماشین مجازی جاوا از بایت کدها استفاده میکند، یک زبان میانی بین زبان جاوا و زبان ماشین است.
کامپایلر جاوا برخلاف زبان برنامه نویسی مانند c که مستقیما کد به زبان ماشین تبدیل میشود، در معماری جاوا ابتدا با Bytecode و سپس از طریق JVM به ساختار ماشین تبدیل میشود. سایز کد کامپایل شده و تبدیل شده به Bytecode معمولا با سایز کد جاوا که توسط برنامه نویس نوشته شده است (Source Code) یکسان است و به راحتی قابل انتقال میباشد.
کد جاوا پس از کامپایل شدن به کلاس فایل (class.) تبدیل میشود که یک فایل binary میباشد که همان Byteocde ها هستند و برای انسان قابل خواندن نیست، برای خواندن این فایل ها بصورت قابل فهم و تبدیل به کد جاوا میتوان از برنامه ای به نام javap که ماشین مجازی جاوا ارائه میدهد استفاده کرد. نتیجه استفاده از javap را Java assemply مینامند.
معماری ماشین مجازی جاوا (JVM)
کد نوشته شده در جاوا با روند زیر اجرا میشود :
برای بارگذاری بایت کدها Class Loader ها این کلاسهای شامل بایت کدهارا در یک منطقه ای به اسم Runtime Data Areas قرار میدهد و موتور اجرا کننده (Execution Engine) بایت کد ها را اجرا میکند.
Class Loader :
جاوا دارای قابلیت بارگذاری پویا میباشد، یعنی در زمان اجرای یک برنامه وقتی که به یک کلاس احتیاج میشود آنرا بارگذاری میکند. که میتوان اصطلاحا Lazy Loading هم نامید که در OR/M هایی مثل Hibernate هم استفاده میشود.
میتوان از قابلیت های کلاس لودر اشاره کرد به :
ساختار سلسله مراتبی : کلاس لودرها در جاوا در یک ساختار سلسله مراتبی میباشند که شامل ساختار پدر و فرزندی میباشد. که بالاتر از همه آنها Bootrstrap Class Loader قرار دارد.
معدل معاوضه ای : بر اساس ساختار سلسله مراتبی بارگذاری شدن کلاس ها بین کلاس لودر ها واگذار میشود یعنی وقتی یک کلاس بارگذاری میشود بررسی میشود که آیا همچین کلاسی در کلاس پدروجود داد یا نه اگر این کلاس وجود داشته باشد و قبلا بارگذاری شده باشد از همان استفاده میکند و در غیر اینصورت یک کلاس جدید بارگذاری میکند.
محدودیت در دسترس بودن : یک کلاس لودر فرزند میتواند در کلاس لودر پدر دردسترس باشد ولی کلاس لودر پدر نمیتواند در کلاس لودر فرزند در دسترس باشد.
کلاس لودر ها فقط میتوانند کلاس ها را بارگذاری کنند :یک کلاس لودر میتواند کلاس هارا بارگذاری کند ولی نمیتواند آنها را خالی (unload) کند و بجای خالی کردن آنها کلاس ها حذف میشوند و کلاس جدید بارگذاری میشود.
هر کلاس لودری فضای دارد که نام کلاس های بارگذاری شده را بر اساس FQCN یعنی Fully Qualified Class Name در آن ذخیره میکند. زمانیکه یک کلاس لودر میخواهد که کلاس ها را بارگذاری کند بر اساس FQCN در نامهای ذخیره شده بررسی میکند که آیا کلاس درخواست شده در حال حاضر بارگذاری شده است یا خیر ؟ حتی اگر کلاس درخواستی در فضای دیگری که مربوط به یک کلاس لودر دیگر است ذخیره شده باشد، آنرا به عنوان کلاسی متفاوت در نظر میگیرد.
ساختار زیر بیانگر ساختار سلسله مراتبی کلاس لودر ها میباشد.
انواع مختلف کلاس لودر ها به شرح زیر میباشند:
Bootstrap Class Loader : این کلاس لودر که در ساختار سلسله مراتبی کلاس لودر ها در بالاترین مرحله قرار دارد و به عنوان پدر همه کلاس لودر ها است زمانی ساخته میشود که JVM اجرا شده و وظیفه آن بارگذاری کردن Java API ها و اشیای ابتدایی جاوا میبشاد و برخلاف بقیه ی کلاس لودر ها بجای جاوا با زبان ماشین پیاده سازی شده است.
Extenstion CLass Loader : وظیفه این کلاس لودر بارگذاری کردن برخی extenstion ها جاوا است مانند Security Extention
Sytem Class Loader : وظیفه این کلاس لودر بارگذاری کردن کلاس های برنامه است و همچنین بارگذاری کردن کلاسهای موجود در CLASSPATH$ که توسط کاربر مشخص میشود است.
User-Defined Class Loader : وظیفه این کلاس لودر اجرا کردن کلاس هایی است که کاربر میسازد و میتوان بصورت پویا در حال اجرای برنامه این کلاس ها را اضافه و یا حذف کرد.
البته framework هایی مثل WAS-Web Application Server برنامه وب را از برنامه اصلی بصورت مستقل اجرا میکند و یا به عبارت دیگر از کلاس لودر های مجزا برای اجرا کردن هر یک از قسمت های وب و منطق برنامه استفاده میکند.
وقتیکه یک کلاس لودر میخواهد کلاسی را براگذاری کند، مراحل زیر انجام میشود :
که شرح آنها به این صورت میباشد :
Loading : کلاس مورد نظر در JVM بارگذاری میشود.
ٰVerifying : در این قسمت بررسی میشود که آیا کلاس بارگذاری شده از مقررات و ساختار جاوا و JVM پیروی میکند یا نه، این قسمت زمان بر ترین و مشکل ترین قسمت لود کلاس ها است که اجازه ی اجرا شدن کلاس را میدهد و یکی از قویترین ویژگیهای جاوا میباشد، چون کد نوشته شده در این قسمت بررسی میشود که مخرب نباشد.
Preparing : حافظه مورد نیاز را به کلاس ها و فیلدها و متدها و ایترفیس تعریف شده در کلاس ها را اختصاص میدهد.
Resolving : تمام مرجع های استفاده شده در این کلاس را به بارگذاری میکند.
Inittializing : متغییر ها استفاده شده در این کلاس را مقدار دهی میکند.
ادامه این بحث را در قسمت دوم ادامه خواهم داد ...