# Устройство 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-архивы.
--

**Компилятор 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)

##### Не примитивные типы данных или ссылочные типы данных
Это данные, которые являются составными(классы, массивы, строки(массив char))
**Примитивные типы vs ссылочные**

#### Приведение типов
Бывает явным и неявным в зависимости от того насколько дружат типы)
*Почитайте в интернете об этом чуть более подробно*
###### Ниже пример кода, когда тип данных изменяется автоматически, то есть неявно
```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)

## Одномерные и двумерные массивы.
**Декларация и создание массивов. Доступ к элементам массива.**
Массив - это ссылочный тип данных, может иметь ноль и более элементов, характеристики массива фиксируются при создании:
- расположение в памяти начального элемента
- длина (количество элементов)
- тип (и размер в байтах) элементов
Работает всё также как и с переменными, пример работы с ними:
```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 многомерные массивы хранятся не как единый непрерывный блок памяти, а как массив массивов. Это означает, что:
- В двумерном массиве каждая "строка" является отдельным одномерным массивом.
- Главный (внешний) массив содержит ссылки на эти внутренние массивы, а не сами данные.
- Каждый внутренний массив хранится в памяти независимо от других.

## Инструкции ветвления (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 (переменное число)** | `(тип... имя)` | Все аргументы одного типа |
## Форматированный вывод числовых данных.

## 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).