# Устройство Java - Всё что нужно знать ## *О чём тут написано?* 1. Язык Java. Особенности языка. 2. Средства разработки. JDK и JRE. Компиляция и выполнение программы. JAR-архивы. 3. Примитивные типы данных в Java. Приведение типов. 4. Работа с переменными. Декларация. Инициализация. Присваивание. 5. Одномерные и двумерные массивы. Декларация и создание массивов. Доступ к элементам массива. 6. Инструкции ветвления (if-else, switch) и циклов (do, while, for). 7. Операторы и выражения в Java. Особенности вычисления, приоритеты операций. 8. Математические функции в составе стандартной библиотеки Java. Класс `java.lang.Math`. 9. Подпрограммы, методы, параметры и возвращаемые значения. 10. Форматированный вывод числовых данных. ----- ## Язык Java. Особенности языка Java — строго типизированный объектно-ориентированный кроссплатформенный язык программирования общего назначения, разработанный компанией Sun Microsystems. > *строго типизированный* - язык требует строгого задания типов данных, для выполнения операций с разными типами данных требуется привести их к одному типу данных. > ПРОЩЕ: Создавая переменную вы должны писать её тип, а не как в питоне)) > *Объектно-ориентированное программирование (ООП)* — это **подход, при котором программа рассматривается как набор объектов(классов и экземпляров), взаимодействующих друг с другом** > *Кроссплатформенный* - программы написанные на языке Java могут работать на любой платформе благодаря JVM(Java Virtual Machine) ## Инфраструктура Java Средства разработки. JDK и JRE. Компиляция и выполнение программы. JAR-архивы. -- ![Pasted image 20251004174753](https://hackmd.io/_uploads/HygTcZJTeg.png) **Компилятор vs Интерпретатор** Компилятор - создаёт машинный код - под конкретную платформу - не нужен при исполнении Интерпретатор - интерпретирует и исполняет программу > **JDK (Java Devolopment kit)** — это набор сервисов, облегчающих процесс написания кода на языке Java. Инструменты JDK нужны для написания кода, его компиляции, отлаживания и запуска. Без них нет возможности полноценно программировать. > > **Пакет состоит из:** > - среды выполнения Java — JRE; > - интерпретатора — java;  - компилятора — javac;  >- архиватора — jar;  - генератора документации — javadoc. >**JRE (Java Runtime Environvement)** минимальная (без компилятора и других средств разработки) реализация [виртуальной машины](https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D0%B0 "Виртуальная машина"), необходимая для исполнения [Java](https://ru.wikipedia.org/wiki/Java "Java")-приложений. Состоит из виртуальной машины [Java Virtual Machine](https://ru.wikipedia.org/wiki/Java_Virtual_Machine "Java Virtual Machine") и [библиотеки Java-классов](https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D0%B0%D1%8F_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0 "Стандартная библиотека") (Java API) > **Java API** - стандартный набор библиотек > **JIT** (Just In Time compilator) - **компилирует в моменте** часто выполняющиеся части кода(циклы и методы) переводит в машинный код и таким образом ускоряет их выполнение. > *Возможно JIT использует hash для проверки на повторяемость результатов выполнения кода* ##### Запуск Java кода или почему Java кроссплатформенный > **Как это работает?** > Вы написали программу на языке Java. Далее вы компилируете программу c помощью javac(Java compiler входит в JDK) на выходе получается файл .class c байт-кодом. (его можно посмотреть с помощью javap). > > Далее, когда вам нужно запустить код вашего Java-приложения, JVM интерпретирует готовый байт-код в машинный код, а с помощью JIT (Just In Time compiler — компилятора «в реальном времени») часто выполняющиеся части кода (циклы и методы) переводятся в машинный код, что ускоряет их выполнение. Благодаря JVM можно выполнять код, написанный на Java, на любой платформе. **!!!Важно!!!** ```bash javap -c YourClassName.class выводит байт код ``` ##### JAR (Java Archive) >— это формат архивирования, основанный на ZIP, предназначенный для объединения скомпилированных файлов Java-классов, ресурсов (таких как изображения, аудио, конфигурационные файлы) и метаданных в один файл с расширением `.jar`. Это упрощает распространение Java-приложений и библиотек, поскольку все необходимые файлы содержатся в одном архиве > >MyProgramm.java => **javac** => MyProgramm.class => **jar** => MyProgramm.jar Файл который определяет откуда будет запускаться ваша программа после создания JAR ``` MANIFEST MANIFEST.MF – Main-Class: Hello ``` ``` bash jar -c -f hello.jar -e Hello *.class ``` ## Примитивные типы данных и приведение типов Примитивные типы в Java — это восемь основных, не-объектных типов данных, встроенных в язык. Они делятся на целочисленные (byte, short, int, long, char), вещественные (float, double(два бита)) для чисел с плавающей точкой и логический тип boolean (true или false), который используется в операциях сравнения. **Примитивные** - Целые (byte, short, int, long) - Символьные (char) - Вещественный (float, double) - Логический (boolean) ![Pasted image 20251004191710](https://hackmd.io/_uploads/ByOlobJ6xl.png) ##### Не примитивные типы данных или ссылочные типы данных Это данные, которые являются составными(классы, массивы, строки(массив char)) **Примитивные типы vs ссылочные** ![Pasted image 20251004202842](https://hackmd.io/_uploads/BJ7GsWJagl.png) #### Приведение типов Бывает явным и неявным в зависимости от того насколько дружат типы) *Почитайте в интернете об этом чуть более подробно* ###### Ниже пример кода, когда тип данных изменяется автоматически, то есть неявно ```java int intValue = 100; long longValue = intValue; // автоматически double doubleValue = intValue; // автоматически float floatValue = longValue; // автоматически ``` ###### Явное приведение или ручное ```java double doubleValue = 100.9; int intValue = (int) doubleValue; // будет 100 (дробная часть отбрасывается) long longValue = 30000; short shortValue = (short) longValue; // может быть потеря данных ``` ## Переменные **Декларация. Инициализация. Присваивание - этапы жизни переменных** ##### 1. Декларация (объявление) Объявление переменной с указанием её типа и имени. ```java // Декларация без значения int age; String name; double salary; ``` ##### 2. Инициализация Первое присваивание значения переменной. ```java // Явная инициализация при объявлении int age = 25; String name = "Иван"; double salary = 50000.0; // Отложенная инициализация int count; count = 10; // инициализация ``` ##### 3. Присваивание Изменение значения уже объявленной переменной. ```java int x = 5; // декларация + инициализация x = 10; // присваивание нового значения x = 15; // еще одно присваивание ``` ### Память в Java https://topjava.ru/blog/stack-and-heap-in-java >Для оптимальной работы приложений JVM использует разные типы памяти, каждая из которых имеет свое назначение и особенности. Всякий раз, когда мы объявляем переменные, создаем объекты или вызываем методы, виртуальная машина выделяет память для этих операций либо в стеке (_stack_), либо в куче (_heap_). ##### **СТЕК** >Всякий раз, когда в Java вызывается новый метод, содержащий примитивные значения или ссылки на объекты, на вершине стека под них выделяется блок памяти, называемый фреймом (_frame, кадр стека_). Этот блок создается в верхней части пространства памяти стека и растет вниз по мере добавления новых данных. Фреймы обычно содержат: - аргументы вызванного метода, которые были ему переданы - зарезервированное пространство под локальные переменные, созданные в методе - ссылки на объекты в куче на которые ссылается метод - адрес возврата, указывающий на строку байт-кода, куда методу следует вернуть результат своей работы - ссылку на предыдущий фрейм (указатель на фрейм вызывающего метода) >Когда метод завершает выполнение, отведенный для его нужд фрейм, очищается (выталкивается), освобождая пространство для нового метода. При этом поток выполнения программы возвращается к месту вызова метода с последующим переходом к следующей строке кода. Именно из-за стека рекурсия бывает выдает ошибку) ##### **КУЧА** >Эта область памяти используется для динамического выделения памяти для объектов и классов JRE во время выполнения. Новые объекты всегда создаются в куче, а ссылки на них хранятся в стеке. Эти объекты имеют глобальный доступ и могут быть получены из любого места программы. **Эта область памяти разбита на несколько более мелких частей, называемых поколениями:** 1. **Young Generation** — область где размещаются недавно созданные объекты. Когда она заполняется, происходит быстрая сборка мусора 2. **Old (Tenured) Generation** — здесь хранятся долгоживущие объекты. Когда объекты из Young Generation достигают определенного порога «возраста», они перемещаются в Old Generation 3. **Permanent Generation** — эта область содержит метаинформацию о классах и методах приложения, но начиная с Java 8 данная область памяти была упразднена. Подробнее об этом можно узнать из [статьи](https://topjava.ru/blog/permgen-and-metaspace), а также посмотрев [видео](https://youtu.be/EMXflWb9Z5w?t=6m47s) Мы можем управлять размерами кучи в зависимости от наших требований. **Минусы кучи:** - Когда эта область памяти полностью заполняется, Java бросает java.lang.OutOfMemoryError - Доступ к ней медленнее, чем к стеку - Эта память, в отличие от стека, автоматически не освобождается. Для сбора неиспользуемых объектов используется сборщик мусора - В отличие от стека, куча не является потокобезопасной и ее необходимо контролировать, правильно синхронизируя код Metaspace (метапространство) — это область памяти в Java Virtual Machine (JVM), которая хранит метаданные классов. Она была введена в Java 8 и заменила собой область, известную как Permanent Generation (PermGen). ##### **КРАТКО** --- Локальные переменные методов живут в стеке, а все объекты создаются в куче. Сборщик мусора автоматически удаляет объекты из кучи, когда на них нет ссылок. **Память в Java** - динамически управляется: - выделяется автоматически - освобождается автоматически - 2 основные области: стек и куча **Стек** - локальные переменные - параметры метода - примитивные значения, а также ссылки на объекты(не примитивы) **Куча** - Объекты (классы, массивы и т. д) - Чиститься сборщиком мусора (Garbage Collector) ![Pasted image 20251004200720](https://hackmd.io/_uploads/Syx4jbJaxg.png) ## Одномерные и двумерные массивы.  **Декларация и создание массивов. Доступ к элементам массива.** Массив - это ссылочный тип данных, может иметь ноль и более элементов, характеристики массива фиксируются при создании: - расположение в памяти начального элемента - длина (количество элементов) - тип (и размер в байтах) элементов Работает всё также как и с переменными, пример работы с ними: ```java // 1. Декларация int[] numbers; String[] names; // 2. Создание с указанием размера numbers = new int[5]; // массив из 5 элементов (0,0,0,0,0) names = new String[3]; // массив из 3 элементов (null,null,null) // 3. Декларация + создание в одной строке double[] prices = new double[10]; // 4. Инициализация с значениями int[] primes = {2, 3, 5, 7, 11}; String[] colors = {"red", "green", "blue"}; // Чтение элементов System.out.println(primes[0]); // 10 (первый элемент) System.out.println(primes[2]); // 30 (третий элемент) // Запись элементов numbers[1] = 25; // было 20, стало 25 numbers[4] = 100; // было 50, стало 100 // Итерация по массиву for (int i = 0; i < numbers.length; i++) { System.out.println(numbers[i]); } // For-each цикл for (int number : numbers) { System.out.println(number); } ``` #### Двумерные массивы ```java // 1. Декларация int[][] matrix; String[][] table; // 2. Создание прямоугольного массива matrix = new int[3][4]; // 3 строки, 4 столбца table = new String[2][3]; // 2 строки, 3 столбца // 3. Декларация + создание double[][] prices = new double[5][5]; // 4. Инициализация с значениями int[][] matrix2 = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; // 5. Зубчатый массив (jagged array) int[][] jagged = new int[3][]; jagged[0] = new int[2]; // первая строка - 2 элемента jagged[1] = new int[4]; // вторая строка - 4 элемента jagged[2] = new int[3]; // третья строка - 3 элемента ``` ```java int[][] matrix = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; // Чтение элементов System.out.println(matrix[0][0]); // 1 (первая строка, первый столбец) System.out.println(matrix[1][2]); // 6 (вторая строка, третий столбец) // Запись элементов matrix[0][1] = 20; // было 2, стало 20 matrix[2][2] = 99; // было 9, стало 99 // Итерация по двумерному массиву for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.print(matrix[i][j] + " "); } System.out.println(); } // For-each для двумерного массива for (int[] row : matrix) { for (int element : row) { System.out.print(element + " "); } System.out.println(); } ``` **Многомерные массивы** Есть два способа хранения многомерных массивов 1 способ - плотный (dense) или регулярный (regular) массив - Для доступа к элементу многомерного массива в этом плоском массиве используется математическая формула, которая преобразует многомерные индексы в один одномерный. - Единый блок хранения в памяти - Высокая производительность - Возможность гибко менять форму и размерность 2 способ (Массив массивов меньшей размерности) - Может храниться частями - Производительность ниже, но зависит от реальной формы - Возможность иметь не только прямоугольную форму **ВАЖНО: В java массивы хранятся, как массив массивов. Это фундаментальное архитектурное решение языка.** В Java многомерные массивы хранятся не как единый непрерывный блок памяти, а как массив массивов. Это означает, что: - В двумерном массиве каждая "строка" является отдельным одномерным массивом. - Главный (внешний) массив содержит ссылки на эти внутренние массивы, а не сами данные. - Каждый внутренний массив хранится в памяти независимо от других. ![Pasted image 20251004203956](https://hackmd.io/_uploads/BkHriZJagx.png) ## Инструкции ветвления (if-else, switch) и циклов (do, while, for). ```java if (number > 0) { System.out.println("Число положительное"); } // if-else if (number % 2 == 0) { System.out.println("Число четное"); } else { System.out.println("Число нечетное"); } // if-else if-else int score = 85; if (score >= 90) { System.out.println("Оценка A"); } else if (score >= 80) { System.out.println("Оценка B"); } else if (score >= 70) { System.out.println("Оценка C"); } else { System.out.println("Оценка D"); } // switch switch (dayOfWeek) { case 1: System.out.println("Понедельник"); break; case 2: System.out.println("Вторник"); break; case 3: System.out.println("Среда"); break; case 4: System.out.println("Четверг"); break; case 5: System.out.println("Пятница"); break; default: System.out.println("Выходной день"); } // Switch с несколькими case (Java 14+) String season = "Лето"; switch (season) { case "Зима", "Весна", "Лето", "Осень" -> System.out.println("Это время года: " + season); default -> System.out.println("Неизвестное время года"); } // Классический for System.out.println("Числа от 1 до 5:"); for (int i = 1; i <= 5; i++) { System.out.println("i = " + i); } // Обратный отсчет System.out.println("Обратный отсчет:"); for (int i = 5; i >= 1; i--) { System.out.println("i = " + i); } // For-each для массива String[] fruits = {"Яблоко", "Банан", "Апельсин"}; System.out.println("Фрукты:"); for (String fruit : fruits) { System.out.println(fruit); } // While с условием int counter = 1; System.out.println("While цикл:"); while (counter <= 5) { System.out.println("Счетчик: " + counter); counter++; } // Do-while (гарантированно выполнится хотя бы 1 раз) int number = 10; System.out.println("Do-while цикл:"); do { System.out.println("Число: " + number); number++; } while (number <= 5); // Условие ложно, но цикл выполнится 1 раз ``` ## Операторы и выражения в Java. Особенности вычисления, приоритеты операций. >Операторы — это специальные символы или ключевые слова, которые выполняют операции над операндами (переменными, константами, выражениями). Выражения комбинируют операторы и операнды для вычисления значения. --- ### **1. Типы операторов** #### **Арифметические** - `+` (сложение), `-` (вычитание), `*` (умножение), `/` (деление), `%` (остаток от деления). - Инкремент/декремент: `++` и `--` (префиксные и постфиксные). - Пример: ```java int a = 5 + 3; // 8 int b = a % 3; // 2 ``` #### **Операторы сравнения** - `==`, `!=`, `>`, `<`, `>=`, `<=`. - Возвращают `boolean`: ```java boolean result = (10 > 5); // true ``` #### **Логические** - `&&` (И), `||` (ИЛИ), `!` (НЕ). - **Сокращённое вычисление (short-circuit)**: - Для `&&`: если левый операнд `false`, правый не вычисляется. - Для `||`: если левый операнд `true`, правый не вычисляется. - Пример: ```java if (x != null && x.getValue() > 10) { ... } // Безопасно ``` #### **Побитовые** - `&`, `|`, `^` (XOR), `~` (НЕ), `<<`, `>>`, `>>>`. - Работают с битами целочисленных типов: ```java int x = 5 & 3; // 1 (101 & 011 = 001) ``` #### **Присваивания** - `=`, `+=`, `-=`, `*=`, `/=`, `%=` и другие комбинированные операторы. - Пример: ```java int x = 10; x += 5; // x = 15 ``` #### **Тернарный оператор** - `условие ? выражение1 : выражение2`. - Пример: ```java int max = (a > b) ? a : b; ``` ### **2. Приоритет операций** Приоритет определяет порядок вычисления. Список от высшего к низшему: | Категория | Операторы | | ----------------- | ---------------------------------------------- | | Постфиксные | `[]`, `()`, `.`, `++`, `--` (после переменной) | | Унарные | `++`, `--`, `!`, `~`, `+`, `-` (унарные) | | Мультипликативные | `*`, `/`, `%` | | Аддитивные | `+`, `-` | | Сдвиги | `<<`, `>>`, `>>>` | | Отношение | `<`, `<=`, `>`, `>=`, `instanceof` | | Равенство | `==`, `!=` | | Побитовые И | `&` | | Побитовое XOR | `^` | | Логическое И | `&&` | | Тернарный | `? :` | | Присваивание | `=`, `+=`, `-=` и т.д. | **Пример:** ```java int result = 10 + 5 * 2; // 5*2 вычисляется первым → 10 + 10 = 20 boolean flag = true || false && false; // && приоритетнее → true || (false) = true ``` ## Математические функции в составе стандартной библиотеки Java. Класс `java.lang.Math` | Категория | Метод | Описание | | :--- | :--- | :--- | | **Основные операции** | `abs(double a)` | Возвращает абсолютное значение (модуль) числа. | | | `max(double a, double b)` | Возвращает большее из двух чисел. | | | `min(double a, double b)` | Возвращает меньшее из двух чисел. | | **Степени, корни, логарифмы** | `pow(double a, double b)` | Возводит число `a` в степень `b`. | | | `sqrt(double a)` | Возвращает квадратный корень числа. | | | `cbrt(double a)` | Возвращает кубический корень числа. | | | `log(double a)` | Возвращает натуральный логарифм (по основанию `e`). | | | `log10(double a)` | Возвращает десятичный логарифм. | | **Тригонометрия** | `sin(double a)`, `cos(double a)`, `tan(double a)` | Стандартные тригонометрические функции (угол в радианах). | | | `toRadians(double angdeg)` | Преобразует угол из градусов в радианы. | | | `toDegrees(double angrad)` | Преобразует угол из радиан в градусы. | | **Округление** | `round(double a)` | Округляет до ближайшего целого числа. | | | `ceil(double a)` | Округляет число вверх ("потолок"). | | | `floor(double a)` | Округляет число вниз ("пол"). | | **Случайные числа** | `random()` | Возвращает случайное `double` число от `0.0` (включительно) до `1.0` (не включительно). | | **Полезные константы** | `Math.PI` | Число π (приблизительно 3.14159). | | | `Math.E` | Число e (основание натурального логарифма, приблизительно 2.718). | #### Примеры использования ```java // Вычисление гипотенузы double sideA = 3.0; double sideB = 4.0; double hypotenuse = Math.hypot(sideA, sideB); // Результат: 5.0 // Возведение в степень и округление double radius = 10.0; double area = Math.PI * Math.pow(radius, 2); long roundedArea = Math.round(area); // Результат: 314 // Генерация случайного числа в диапазоне [min, max] int min = 10; int max = 20; int randomNum = (int) (Math.random() * ((max - min) + 1)) + min; ``` ## Подпрограммы, методы, параметры и возвращаемые значения. **Подпрограммы** - именованная часть кода, которая может выполняться по вызову. Уменьшает количество строк кода в программе и улучшает читаемость. **Функция** - подпрограмма, не привязанная к методу **Методы** - подпрограмма, которая в отличии от функции привязана к классу или объекту. **Параметры** - специальные переменные метода или функции передаваемые вовремя вызовы. | Тип параметров | Синтаксис | Особенности | | ------------------------------ | ------------------------ | ------------------------------ | | **Обычные параметры** | `(тип1 имя1, тип2 имя2)` | Каждый параметр имеет свой тип | | **Varargs (переменное число)** | `(тип... имя)` | Все аргументы одного типа | ## Форматированный вывод числовых данных. ![Pasted image 20251004230133](https://hackmd.io/_uploads/H1nIs-16xx.png) ## Metaspace, ClassLoader, JIT ### 1. Metaspace (Метасространство) и ClassLoader (Загрузчик классов) **Metaspace** — это область памяти в JVM (заменившая собой старую **PermGen** начиная с Java 8), где хранится **метаинформация о классах**. * **Что хранится:** Определения классов (имя, методы, поля, аннотации), константный пул (constant pool), информация для JIT-компиляции и т.д. * **Важно:** Это не память под сами объекты (объекты лежат в Heap/Кучи), а их "чертежи". * **Управление памятью:** Metaspace автоматически расширяется (в рамках ограничений, которые можно задать) и собирается сборщиком мусора (Garbage Collection), когда класс выгружается. Класс выгружается, когда его загрузивший ClassLoader и все его экземпляры становятся недостижимыми. **ClassLoader (Загрузчик классов)** — это часть JVM, которая отвечает за динамическую загрузку, связывание и инициализацию классов. * **Иерархия:** Работают по принципу делегирования. Есть Bootstrap ClassLoader (загружает системные классы), Extension ClassLoader (загружает классы расширений) и Application ClassLoader (загружает классы вашего приложения). * **Связь с Metaspace:** Когда ClassLoader загружает класс, он помещает всю его метаинформацию в Metaspace. Каждый класс "помнит", какой ClassLoader его загрузил. **Взаимосвязь:** ClassLoader'ы загружают байт-код классов, а метаинформация об этих классах хранится в Metaspace. JIT-компилятор (C1/C2) использует эту метаинформацию для компиляции. --- ### 2. C1 и C2: Как JIT компилирует методы и циклы JIT (Just-In-Time) — компиляция "на лету". Вместо того чтобы интерпретировать байт-код снова и снова, JVM компилирует часто выполняемые участки кода в нативный машинный код для процессора. В HotSpot JVM используется **Многоуровневая компиляция (Tiered Compilation)**, которая объединяет два компилятора: * **C1 (Клиентский компилятор):** * **Цель:** Скорость компиляции, минимальные затраты, быстрое стартовое время. * **Оптимизации:** Базовые (inlining, удаление неиспользуемого кода, несложные оптимизации циклов). * **Используется на** уровнях 1, 2 и 3 многоуровневой компиляции. * **C2 (Серверный компилятор):** * **Цель:** Максимальная производительность (пиковая), даже ценой более долгой компиляции. * **Оптимизации:** Очень агрессивные и сложные. Основаны на **профилировании** (сборе статистики во время работы). * **Примеры оптимизаций C2:** Инлайнинг виртуальных методов, размотровка циклов, анализ эскейп-анализа (определение, что объект не "убегает" за пределы метода и может быть размещен на стеке), удаление границ проверок массивов и т.д. * **Используется на** уровне 4. #### Процесс компиляции методов и циклов 1. **Интерпретация и подсчет.** * Изначально все методы выполняются в **интерпретаторе**. * У каждого метода есть два счетчика: * **Счетчик вызовов** (invocation counter): Увеличивается при каждом входе в метод. * **Счетчик обратных ветвлений** (backedge counter): Увеличивается при каждом завершении итерации цикла (когда выполнение "перепрыгивает" назад к началу цикла). 2. **Обнаружение "Горячего" кода (HotSpot).** * JVM постоянно следит за этими счетчиками. Когда они превышают определенный порог, метод или цикл помечаются как "горячие" (**hot spot** — отсюда и название HotSpot JVM). * **Метод становится "горячим"** при большом количестве вызовов. * **Цикл становится "горячим"** при большом количестве итераций (даже внутри одного вызова метода!). 3. **Эскалация компиляции (Tiered Compilation).** * **Уровень 3 (C1 с полным профилированием):** Сначала "горячий" метод компилируется компилятором **C1**. На этом этапе C1 активно собирает детальную профилировочную информацию: какие типы объектов реально используются, какие ветки кода выполняются чаще и т.д. * **Уровень 4 (C2 с агрессивными оптимизациями):** Когда набирается достаточно профилировочных данных, в дело вступает **C2**. Он использует эти данные для проведения глубоких оптимизаций. #### Специфические оптимизации для циклов * **Размотровка цикла (Loop Unrolling):** C2 может "развернуть" цикл, чтобы уменьшить количество проверок условий. * **Было:** ```java for (int i = 0; i < 1000; i++) { doSomething(i); } ``` * **Стало (условно, в нативном коде):** ```java for (int i = 0; i < 1000; i += 4) { doSomething(i); doSomething(i+1); doSomething(i+2); doSomething(i+3); } ``` Это снижает накладные расходы на проверку `i < 1000` и инкремент `i++`. * **Вынос инвариантов (Loop Invariant Code Motion):** Если внутри цикла есть вычисления, результат которых не меняется от итерации к итерации, C2 выносит их за пределы цикла. * **Было:** ```java for (int i = 0; i < items.length; i++) { result = items[i] * Math.PI; // Math.PI - константа } ``` * **Стало:** ```java double constantPi = Math.PI; for (int i = 0; i < items.length; i++) { result = items[i] * constantPi; } ``` * **Слияние циклов (Loop Fusion):** Если подряд идут два цикла с одинаковыми условиями, C2 может объединить их в один, чтобы уменьшить количество проходов. * **Замена на intrinsics:** Очень маленькие и часто используемые циклы (например, `System.arraycopy`) JVM может заменить на предопределенный высокооптимизированный машинный код (intrinsic). ### Краткий итог процесса: **Интерпретатор → Сбор профиля → C1 (быстро) → Больше профиля → C2 (медленно, но очень эффективно) → Агрессивные оптимизации (размотровка циклов и т.д.)** Эта сложная система позволяет Java-приложениям начинать работу быстро (благодаря интерпретатору и C1), а со временем достигать производительности, сравнимой с скомпилированным C++ кодом (благодаря C2).