[00:02] удобочитаемый исходный код. Но когда мы запускаем программу, за кулисами происходит множество процессов, преобразующих исходный код в необработанные аппаратные инструкции. В основе всего этого лежит виртуальная машина Java, или JVM. JVM — это движок, который загружает [00:15] байт-код Java, управляет памятью и оптимизирует выполнение на лету. программы на Java от исходного файла до машинного Первый этап, этап строительства. Прежде чем запустится JVM, нам необходимо скомпилировать [00:30] код. Компилятор Java берет исходные файлы .java и преобразует их в файлы .class. Эти файлы содержат два важных компонента. Во-первых, они содержат байт-код Java. Байт-код Java — это платформенно-независимый набор инструкций, [00:44] специально разработанный для JVM. Один и тот же байт-код может быть передан виртуальной машине Java (JVM) в macOS, Linux или Windows, и эта JVM преобразует его в инструкции, которые может выполнить локальный компьютер. В этом и заключается основополагающий принцип Java: [00:58] напиши один раз, запускай где угодно. Во-вторых, файлы .class содержат метаданные. Метаданные описывают классы, а также их методы, модификаторы доступа и типы переменных. Байт-код и метаданные могут храниться в виде [01:12] отдельных файлов .class, могут быть включены в JAR-архив или находиться внутри Java-модуля. Упаковка может различаться, но JVM по-прежнему ищет одно и то же . Инструкции байт-кода и метаданные, описывающие их. [01:25] Сегодняшнее видео спонсируется компанией Code Rabbit, самым популярным приложением для искусственного интеллекта на GitHub и GitLab. В наши дни один сгенерированный ИИ запрос на слияние может затрагивать десяток совершенно несвязанных вещей, а для его проверки приходится переключаться между вкладками. [01:38] Новый интерфейс проверки кода в Code Rabbit исправляет эту проблему. Это разбивает разницу на небольшие, связанные между собой фрагменты, которые можно просматривать по одному. Щёлкните по любой функции, и её определение появится прямо там. Есть вопросы по поводу изменений? Просто [01:52] покидая страницу. И это позволяет в первую очередь выявлять критически важные моменты, что сокращает время проверки и количество ошибок вдвое. Начните бесплатную 14-дневную пробную версию. Ссылка в описании. Второй этап: подсистема загрузки классов. [02:07] виде байт-кода, JVM не загружает всё приложение в память сразу. Вместо этого, как правило, классы загружаются по мере необходимости для выполняемого кода. Если определенный класс ни разу не использовался во время выполнения программы, он может никогда не быть загружен. Ленивая [02:22] загрузка осуществляется подсистемой загрузчика классов . Она основана на наборе правил, называемых моделью делегирования родительских полномочий. Когда приложение запрашивает класс, запрос вышестоящему руководству. Загрузчик классов верхнего уровня [02:35] получает первоочередное право на загрузку дополнительных классов Java, таких как java.lang.Object. Если класс не найден, запрос передается загрузчику классов платформы , который обрабатывает другие классы платформы Java и JDK . [02:49] Наконец, если он по-прежнему не найден, он обращается к загрузчику классов приложения, который загружает код приложения из пути к классам или пути к модулям. Такой подход, при котором сначала выполняется поиск родительского элемента, помогает защитить среду выполнения. Это затрудняет [03:01] замену основных классов Java вредоносными версиями в коде приложения. Третий этап: связывание и инициализация. После того как файл класса найден и загружен, JVM устанавливает с ним связь. Процесс связывания состоит из трех [03:15] Первый шаг — проверка. Здесь JVM проверяет корректность байт-кода . Он проверяет, не повредит ли код внутренний стек и не попытается ли выполнить недопустимое приведение типов, например, притвориться, что целое число является ссылкой на объект. [03:29] Второй шаг — подготовка. Здесь JVM выделяет память для любых статических полей и присваивает им значения по умолчанию. Например, числовые типы данных устанавливаются в ноль, а ссылки на объекты — в значение null. [03:42] Третий шаг — разрешение конфликта. В байт-коде Java изначально используются символические имена для ссылки на другие классы и методы. JVM преобразует эти символические имена в прямые ссылки во время выполнения, которые она может использовать. Некоторые ссылки могут быть исправлены [03:56] в процессе связывания. Другие проблемы могут быть решены позже, при первом использовании. Затем, наконец, следует инициализация. Здесь эти статические поля получают свои фактические значения, определенные в коде, и выполняются все блоки статической инициализации [04:08] . Четвертый этап, области данных во время выполнения. инициализации классов и начала выполнения методов, JVM хранит состояние программы в наборе областей данных времени выполнения. Общие пространства доступны для всех [04:23] потоков. Там у нас находится куча и область методов. Куча — это большое рабочее пространство, в котором находятся все объекты. Концептуально, область методов хранит структуры уровня класса, такие как метаданные, информация о методе, статические переменные [04:37] и пул констант времени выполнения. В рамках попоточного распределения памяти каждый поток получает свой собственный выделенный блок памяти. Это включает в себя стек JVM. При каждом вызове метода в стек помещается новый кадр для хранения локальных переменных и частичных [04:49] Каждый поток также отслеживает свой прогресс с помощью регистра счетчика команд. байт-код Java, он указывает на текущую инструкцию JVM. Если Java взаимодействует с нативным кодом через Java Native Interface, поток [05:04] . Пятый этап – исполнение. Но как же байт-код на самом деле превращается в аппаратное исполнение? Точная стратегия выполнения зависит от реализации JVM. [05:19] Большинство работающих в производственной среде JVM, включая HotSpot, используют гибридный механизм выполнения для баланса между скоростью и временем запуска. При запуске интерпретатор считывает байт-код по одной инструкции за раз и мгновенно выполняет его. Благодаря этому [05:32] приложение запускается быстро, поскольку отсутствует задержка, связанная с ожиданием компиляции байт-кода в машинный код, но интерпретация происходит относительно медленно. Для ускорения этого процесса JVM постоянно отслеживает поведение кода во время его [05:44] выполнения. Если определенный фрагмент кода выполняется часто, JVM помечает его как « горячую точку». Компилятор "на лету" захватывает наиболее часто используемый байт-код и компилирует его непосредственно в машинный код, оптимизированный для [05:57] конкретного процессора, а затем сохраняет его в кэше кода. В следующий раз, когда этот метод будет вызван, JVM сможет использовать скомпилированную версию вместо Во время работы программы объекты постоянно создаются или [06:11] удаляются из кучи. Когда объект становится недоступным из корневых каталогов сборщика мусора, он становится пригодным для сборки мусора. Мусорщик выслеживает эти недоступные объекты и позже восстанавливает их память. [06:23] Это избавляет разработчиков от необходимости вручную управлять памятью. Компромисс заключается в том, что или задержки в зависимости от сборщика и рабочей нагрузки. Это затрудняет понимание работы программ, чувствительных к задержкам . [06:36] В основе работающей программы на Java лежат все эти компоненты, взаимодействующие друг с другом. Это these pieces are working together. This is the JVM in motion.