Перш за все нам треба познайомитись з трьома важливими абревіатурами - JVM,JDK і JRE. Найростіше це, мабуть, зробити за допомогою ось такого веселого малюнка:
Тобто JVM - це Java Virtual Machine - основний компонент середовища виконання нашого коду - віртуальна машина, яка виконує проміжний - байт-код (пам’ятаєте ми говорили, що Java - це мова подвійної компіляції?), який отримується у результаті компіляції наших файлів з вихідними кодами.
JDK - це Java Development Kit - все, що вам необхідно для розробки програм мовою Java - інструменти, документація, зразки коду, середовище виконання (JVM), бібліотека стандартних класів (JRE)…
JRE - це Java Runtime Environment - все, що вам необхідно для виконання програм, побудованих з використанням стандартних будівельних блоків - набір (бібліотека, Java API) класів, які ви можете використовувати в своїх додатках. Тож велосипед винаходити вже не потрібно - все вже написано до нас - JRE надає сотні тисяч готових класів, які вже реалізують найрізноманітнішу стандартну функціональність.
Віртуальна машина Java - це додатковий рівень абстракції над ОС та “залізом” ПК. Саме вона виконує ваш байт-код, тобто дозволяє вашому додатку виконуватись незалежно від того, на якій платформі побудований та під керуванням якої ОС працює ваш ПК. По суті, віртуальна машина - це уявне, емульоване на реальній машині, стандартне середовище виконання для ваших .class-файлів, отриманих в результаті компіляції .java-файлів. В процесі роботи віртуальна машина контролює всі аспекти виконання коду - перевіряє байт-код, завантажує його, виконує купу перевірок безпеки, сумісності, керує пам’яттю тощо.
Ще один важливий момент - це наявність Специфікації віртуальної машини Java, яка регламентує всі аспекти емуляції середовища виконання байт-коду Java. Ідея полягає в тому, шоб стандартизувати різні віртуальні машини від разних виробників - будь-яка віртуальна машина, яка відповідає специфікації, має бути здатною виконати будь-який .class - файл. Наступний малюнок містить перелік аспектів, які регламентує специфікація:
Таким чином, шлях від вихідного коду до виконуваної програми виглядає досить простим - ви пишете код на Java і зберігаєте його у файлі з розширенням .java, потім компілятор перетворює його на байт-код (.class-файл), який і виконує віртуальна машина:
В цьому ланцюжку можуть бути й додаткові ланки, такі як компресія байт-коду тощо, однак, поки що нас це не надто хвилює.
До речі, код машина інтерпретує, тобто читає код з файлу та виконує його порядково - на відміну від компілятора, який переглядає весь проміжний код, перетворює його на машинні коди, а вже потім запускає його на виконання - як це робить конкуруюча технологія - .NET. Тому Java-програми завантажуються швидше, однак виконуються дещо повільніше за .NET-програми, які завантажуються значно довше, проте виконуються швидше. Раніше ми також говорили, що віртуальна машина виконує купу інших обов’язків - завантажує класи з відповідних .class-файлів, перевіряє їх на валідність, звільняє пам’ять від непотрібних більше об’єктів (прибирає сміття) тощо. Для виконання цих завдань у складі віртуальної машини є відповідні компоненти:
Про збір сміття ми ще будемо далі говорити детальніше. Поки що просто зазначимо, що від того наскільки ефективним є алгоритм, за яким побудовано збирач сміття (Garbage Collector) дуже сильно залежить швидкодія виконання Java-програм віртуальною машиною взагалі. Ідея збирача сміття дуже проста - програміст просто створює необхідні йому об’єкти і використовує їх - все! Його не цікавлять “низькорівневі” аспекти вивільнення пам’яті від непотрібних більше об’єктів - це робиться автоматично й відволікатись на це не потрібно - краще зосередитись на логіці роботи додатку! Проста і зрозуміла аналогія - автоматична коробка передач в автомобілі - наше завдання - зосередитись на керуванні рухом машини та слідуванні правилам дорожнього руху, а перемикати передачі - це не справа водія, йому не варто на це відволікатись - машина має робити це самостійно. В інших мовах (наприклад, С++) збір сміття - це обов’язок самого програміста - все, що створили, після використання ви маєте й знищити. В Java ж це робить збирач сміття, який працює у виділенному системному потоці і відслідковує виділення ресурсів (оперативної пам’яті). Його основне завдання - знайти та вивільнити пам’ять, яка більше не потрібна. Працює він у повністю автоматичному режимі й передбачити коли саме він “вийде на полювання” неможливо. Реалізація збирача сміття дуже сильно різниться у різних віртуальних машинах від різних виробників (в Android вона також, на жаль, не є ідеальною).
пам’ятаєте ми говорили про JDK - набір всього найнеобхіднішого для розробки Java-застосунків? До нього ми ще повернемось пізніше, а поки що зауважимо, що основною частиною JDK є JRE - середовище виконання додатків (яке в свою чергу включає в себе віртуальну машину Java - JVM). Які завдання виконує JRE? Та, в принципі, ті ж, що й віртуальна машина:
Окрім віртуальної машини до складу середовища виконання входять інші компоненти, зокрема, завантажувач коду, який власне й завантажує усі необхідні для виконання програми класи, забезпечує (підтримує) використання пакетів (про них ми ще поговоримо - це приблизний аналог “просторів імен” у .NET), запобігає спуфінгу (підміні класів або програм іншими, яким не можна довіряти).
Перевіркою коду займається інший важливий компонент - верифікатор байт-коду. Його можна порівняти з охоронцем на вході до нічного клубу - його завдання - так званий “фейс-контроль” - виявлення осіб, яких не варто пускати до закладу. Так само й верифікатор байт-коду виконує безліч перевірок коду, перш ніж дозволити його виконання. Він перевіряє:
Підводячи підсумки, можна виділити таку послідовність дій, які виконуються при запуску Java-програми:
Якщо деякі терміни вам поки що незнайомі - не засмучуйтесь - ми обов’язково про них поговоримо в подальших лекціях!