نحوه ی کار بایت کد در جاوا
نحوه ی کار بایت کد در جاوا
جاوا یک زبان یا بهتر بگوییم یک فناوری برنامه نویسی است، این زبان برنامه نویسی یکی از پرکاربردترین زبانهای برنامه نویسی در تمام دنیا و ایران است. از جمله ویژگیهای این زبان که باعث محبوبیت آن میشود رایگان بودن استفاده از زبان جاوا برای همگان و آزاد بودن متن پیادهسازی این زبان است که به صورت متن باز یا Open source میباشد. در این مقاله از آموزش جاوا قصد داریم به یکی از مهمترین فاکتور اجرای زبان برنامه نویسی جاوا یعنی بایت کد و نحوه اجرای آن بپردازیم. با ما همراه باشید.
بایت کد در جاوا چیست؟
بایت کد در جاوا به مجموعه دستورالعملهای ماشین مجازی جاوا یا به اختصار JVM اطلاق میشود. در حقیقت باید بگوییم که یک برنامه نویس جاوا نیازی به درک دقیق جاوا بایت کد ندارد اما درک بایت کد و آنچه که توسط کامپایلر جاوا ایجاد میشود به برنامه نویسان کمک بسیاری میکند، همانطور که درک و دانستن در مورد اسمبلی میتواند برای برنامه نویس C یا C++ مفید باشد.
هر بایت کد از یک بایت که نشانگر آپ کد (opcode) است همراه با صفر یا تعداد بایت بیشتر برای عملوندها تشکیل میشود.
دستورالعملها در بایت کدها به چند گروه تقسیم میشوند که شامل موارد زیر است:
- بارگذاری و ذخیره (مانند aload_0 ، istore)
- ریاضیات و منطق (مانند ladd ، fcmpl)
- تبدیل نوع (به انگلیسی: Type conversion) (مانند i2b ، d2i)
- ایجاد و دستکاری شی (Operand stack management)(new ، putfield)
- مدیریت پشته عملوند (مانند swap ، dup2)
- انتقال کنترل (مانند ifeq ، goto)
- فراخوانی تابع و بازگشت (مانند invokespecial, areturn)
ضمن این که چندین دستورالعمل برای تعدادی از کارهای تخصصیتر مانند پرتاپ استثنا (exception throwing)، هماهنگ سازی (synchronization) و غیره وجود دارد. بسیاری از دستورالعملها دارای پیشوند یا پسوندهای مربوط به انواع عملوندهایی که روی آن عمل میکنند نیز هستند.
مفسر جاوا چگونه کار میکند؟
برای این که کاربرد مفسر جاوا را به درستی درک کنید باید در ابتدا با مفهوم مفسر آشنا شوید. در حقیقت مفسر یک برنامه کامپیوتری است که دستورهای نوشته شده در یک زبان برنامه نویسی را اجرا میکند.
به زبان ساده تر مفسر در واقع یک زبان برنامه نویسی سطح بالا را به زبان قابل فهم برای ماشین تبدیل میکند و نقش واسطه دارد. نوشتن یک برنامه به زبان سخت افزار بسیار وقتگیر است، ضمن این که اشکالزدایی این زبانها نیز سخت و طاقت فرسا است. بنابراین به طور معمول از انواع زبانهای برنامه نویسی استفاده شده و با برنامههای خاصی آنها را به زبان ماشین یا سخت افزار تبدیل میکنند. در صورت استفاده از این زبان برنامه نویسی مدیریت خطا در جاوا نیز بسیار آسان تر صورت می گیرد.
در صورتی که این برنامه خاص به جای اجرای یکباره دستورات برنامه نویسی شده با زبان ماشین آن را به شکل دستور به دستور انجام دهد، و هر دستور را در زمان اجرا به پردازنده تحویل دهد و بعد سراغ دستور بعدی و ارسال برای پرازنده کند و تا انتها تمام دستورات برنامه نویسی شده را به همین شکل خط به خط به دستورات قابل فهم برای سخت افزار تبدیل کند به آن برنامه مفسر یا اینترپرتر گفته میشود.
می توان گفت که از مهترین مزایای مفسر کاهش وابستگی برنامه نوشته شده به نوع سخت افزار است. بنابراین برنامه نوشته شده به زبان مفسر قابلیت اجرا روی سخت افزارهای مختلف از جمله پردازنده اینتل کامپیوتر، پردازنده آرم تبلت، پردازنده روی موبایل یا بازیهای کامپیوتری را خواهد داشت.
کامپایلر جاوا چگونه کار میکند؟
در پاسخ به سوال کامپایلر چیست باید بگویم که کامپایلر مجموعهای از برنامه یا برنامههای کامپیوتری است که متنی از زبان برنامهنویسی سطح بالا (زبان مبدا) را به زبانی سطح پایین (زبان مقصد)، مثل اسمبلی یا زبان سطح ماشین، تبدیل میکند.
در واقع می توان گفت که کامپایلر برنامهای است که یک برنامه نوشته شده در یک زبان خاص ساختیافته و پیشرفته را خوانده و آن را به یک برنامه مقصد (Target Language) به منظور اجرا تبدیل میکند. در یکی از مهمترین فرآیندهای این تبدیل، کامپایلر وجود خطا را در برنامه مبدأ اعلام میکند.
کامپایلر برای کامپایل کردن به واحدهایی (Compilation Unit) نیاز دارد. سپس این واحدها را کامپایل کرده و با استفاده از FileManager در محلی ذخیره میکند. شاید ما نیاز داشته باشیم برای Compile کدمان یک سری پارامترهای ورودی نیز داشته باشیم.
کامپایلر برای تعریف یک وظیفه کامپایل (Compilation Task) باید قادر به دریافت پارامترها نیز باشد. در آخر، کدی که برنامه نویس برای کامپایلر ارسال میکند، ممکن است خطای کامپایل داشته باشد، برای همین Compilation Task باید بداند این خطاها را باید به کجا ارسال کند.
جاوا برای کارهایی از قبیل کامپایل کردن در زمان اجرا، واسطی را برای نمایش فایل به نام FileObject در نظر گرفته است. FileObject تنها نمایانگر یک فایل نیست بلکه منظور از فایل هرگونه منبعی از دادهها است. این منبع میتواند یک مقدار Cache در RAM سیستم باشد یا دادهای در Database یا یک فایل معمولی در هارد دیسک از نظر ماهیتی تفاوتی میان این موارد وجود نخواهد داشت. واسط FileObject یک زیر واسط دیگر به نام JavaFileObject دارد که به طور خاص نمایانگر source code کلاسها و خود فایل کلاسها میباشد.
در واقع برای کامپایل کردن در زمان اجرا بایستی شیئی از جنس Iterable که روی فرزندان واسط JavaFileObject پیمایش میکند را به کامپایلر مورد بحث تحویل داد.
اگر اجرای بایت کد جاوا در یک ماشین مجازی جاوا اختلال داشته باشد، یک توسعه دهنده میتواند کد منبع جاوا یا بایت کد را بهطور مستقیم به کد ماشین با ابزارهایی مانند کامپایلر GNU برای جاوا (GCJ) کامپایل کند. بعضی از پردازندهها میتوانند بایت کد جاوا را بهطور بومی نیز اجرا کنند. چنین پردازنده هایی پردازندههای جاوا یا کامپایلرهای جاوا نامیده میشوند.
کامپایلر و مفسر (interpreter) در جاوا
برنامه جاوا در پایان مراحل توسعه مانند یک زبان کامپایلری به جاوا بایت کد کامپایل می شود و در زمان اجرا همانند زبانهای مفسری interpret عمل می کند. پس میتوان گفت زبان جاوا هم یک زبان مفسری است هم مانند زبانهای کامپایلر عمل میکند.
یک کد میانی شبیه به کد ماشین است که پلتفرمهای مختلفی مثل جاوا و .Net برای رفع مشکل قابل حمل نبودن کد ماشین آن را ایجاد کردند. مراحل اجرای کار نیز به این صورت است که به جای اینکه کدهای زبان برنامه نویسی به کد ماشین کامپایل شود به bytecode کامپایل میشود.
تعریف JIT compilation را میتوان به این شکل ارائه کرد که قابلیت کامپایل داینامیک و در زمان اجرای bytecode به کد ماشین توسط ماشین مجازی را JIT compilation میگویند که باعث می شود کدهای میانی توسط ماشین مجازی سریعتر اجرا شوند. ماشین مجازی به جای اینکه خط به خط bytecode را تفسیر کند ابتدا آن را در زمان اجرا به کد ماشین کامپایل می کند و در نهایت آن را اجرا میکند. این کار باعث می شود که بایت کد سریعتر توسط ماشین مجازی اجرا شود.
چگونه کد جاوا در سیستم عامل های مختلف اجرا میشود؟
یک تعریف جامع وجود دارد که می گوید جاوا مستقل از سیستم عامل است در تشریح این تعریف باید گفت که زبانهای برنامه نویسی به طور عمده به دو دسته تقسیم میشوند:
- زبانهای کامپایلری (compiled language) مانند ada, خانواده C , ...
- زبانهای مفسری (interpreted language) مانند php, ruby, ...
در زبانهای کامپایلری نحوه کامپایل شدن برنامه به این شکل است که سورس کد به کامپایلر و کد ماشین به OS تبدیل میشود. یعنی به زبان سادهتر کد برنامه به کامپایلر داده میشود، کامپایلر کد را به کد زبان ماشین تبدیل میکند و سیستم عامل در نهایت کد ماشین را اجرا میکند. این کد ماشین وابسته به سیستم است و کدهای کامپایل شده در یک محیط، در محیط متفاوت دیگر کار نمی کند.
اما در زبانهای مفسری روند اجرای برنامه به این شکل است که کد به مفسر داده میشود اما مفسر خودش برخلاف آنچه در کامپایلر دیدیم کدی تولید نمیکند و کدها را خط به خط اجرا میکند.
اما برای جاوا موضوع بسیار متفاوت است به این معنا که کدها توسط javac compiler کامپایل شده و خروجی به شکل Byte code داده میشود. بایت کدها توسط مفسر جاوا، تفسیر و خط به خط اجرا میشوند.
در سیستمی که JVM روی آن نصب شده است نگرانی درمورد اجرای برنامه وجود ندارد چون JVM وظیفه دارد که برنامه را با محیطی که قرار است روی آن اجرا شود تطبیق دهد. برای سیستم عامل های معروف ویندوز، لینوکس، مکینتاش و ... شرکت اوراکل مفسر های مخصوص را تولید کرده است.
پس مستقل از پلتفرم بودن به این معنا خواهد بود که برنامههای جاوا بر روی سیستم عاملهای معروفی که شرکت اوراکل مفسر آنها را ارایه کرده است قابل اجرا هستند. در نتیجه اگر شخص یا گروهی سیستم عاملی تولید کنند، برنامههای جاوا روی آنها اجرا نمیشوند مگر این که زبان مفسر آن نیز تولید شود.