Анализ побега JVM, который вы должны знать

Java

Когда дело доходит до JVM, я считаю, что все должны знать, что такое JVM? Однако, когда дело доходит до анализа побега, я полагаю, что большинство людей могут запутаться: что же такое анализ побега? Позвольте мне поделиться с вами дальше.

В системе компиляции Java в процессе преобразования файла исходного кода Java в исполняемую компьютером машинную инструкцию необходимо пройти два этапа компиляции.Первый этап компиляции заключается в компиляции файла Java в файл класса, может быть распознан JVM с помощью команды javac. Второй этап компиляции заключается в преобразовании файла класса в инструкции байт-кода для распознавания компьютером.

На втором этапе компиляции JVM переводит байт-код в соответствующие машинные инструкции, интерпретируя их, считывает их одну за другой, интерпретирует и переводит одну за другой. Очевидно, что после интерпретации и выполнения его скорость выполнения будет намного ниже, чем при выполнении программ с двоичным байт-кодом. Это функция традиционного интерпретатора JVM. Для решения этой проблемы эффективности была внедрена технология JIT (Just-In-Time Compilation).

С введением технологии JIT, хотя программы Java по-прежнему интерпретируются и выполняются через интерпретатор, когда определенный метод вызывается часто, JVM будет считать его «горячим кодом». Итак, если бы это были вы, что бы вы подумали о том, чтобы сделать это? Эх, да, кешируйте "горячий код". JIT также делает это: JIT переводит некоторый «горячий код» в локальный машинный код, оптимизирует его, а затем кэширует переведенный машинный код для следующего использования.

Стратегия выделения памяти JVM

В JVM память, управляемая JVM, включает область методов, стек виртуальной машины, собственный стек методов, кучу, счетчик программ и т. д. (Я не буду их здесь вводить по одному, позже напишу статьи по JVM для заинтересованных студентов)

Как правило, данные времени выполнения JVM хранятся в стеке и куче. Стек используется для хранения ссылок на некоторые базовые переменные и объекты (конечно, это не абсолютно и будет введено позже), а куча используется для хранения элементов и объектов массива, то есть конкретных экземпляров новый.

С развитием JIT-компиляторов и зрелостью технологии escape-анализа. Размещение в стеке, технология оптимизации скалярной замены приведут к утверждению, что размещение объектов в куче не так уж и абсолютно.

Что такое анализ побега?

Escape-анализ заключается в том, что когда объект новый, его можно вызывать извне, а если он передается в качестве параметра наружу, то это называется escape-методом.

Например:

Экранирование без метода:

public static void returnStr(){
	User user = new User();
	user.setId(1);
    user.setName("张三");
    user.setAge(18);
}

public static String returnStr(){
	User user = new User();
	user.setId(1);
    user.setName("张三");
    user.setAge(18);
    return user.toString();//这里User要实现get,set方法,还要实现toString方法
}

Выход из метода:

public static User returnStr(){
	User user = new User();
	user.setId(1);
    user.setName("张三");
    user.setAge(18);
    return user;//这里User要实现get,set方法
}

Вы должны увидеть разницу. Два метода в первом абзаце здесь не экранируются, но метод во втором абзаце экранируется. Это показывает, что если вы хотите экранировать метод, вам нужно позволить самому объекту вызываться извне.

Используя escape-анализ, компилятор может оптимизировать код следующим образом:

  1. Синхронизация отсутствует. Если обнаружено, что объект доступен только из одного потока, то операции над этим объектом могут выполняться независимо от синхронизации.
  2. Преобразование выделения кучи в выделение стека. Если объект выделяется в подпрограмме, чтобы указатель на объект никогда не исчезал, объект может быть кандидатом на выделение в стеке, а не в куче.
  3. Разделение объектов или скалярная замена, некоторые объекты могут не существовать как непрерывная структура памяти для доступа, тогда часть (или весь) объекта может храниться не в памяти, а в регистрах ЦП.

Здесь включите и выключите анализ побега с помощью этого:

-XX:+DoEscapeAnalysis: Указывает, что включен анализ побега.

-XX:-DoEscapeAnalysis: Указывает, что escape-анализ отключен.Начиная с jdk 1.7 escape-анализ запущен по умолчанию.Чтобы его отключить, нужно указать -XX:-DoEscapeAnalysis

Фактический бой: наблюдайте, выделяется ли он в стеке, печатая GC

public class Test {

    public static void main(String[] args) {
        Test test = new Test();
        test.createUser();
        System.out.println("11");
    }

    public void createUser(){
        int i = 0;
        while(true){
            User user = new User();
            user.setId(i);
            user.setName("张三");
            user.setAge(18);
            i++;
        }
    }
}

public class User {

    private int id;
    private String name;
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Здесь есть два класса, первый — это класс Test, а другой — класс сущности User.В классе Test бесконечный цикл используется для создания объекта User и настройки его в идее-XX:+PrintGCПараметры, отвечающие за печать информации GC, запуск класса

результат:Здесь видно, что программа не завершилась, но информация GC не распечаталась, в это время отключаем escape-анализ. Настройте эти два в идее-XX:+PrintGC -XX:-DoEscapeAnalysis

начать урок зановоЗдесь видно, что консоль выводит информацию GC, поэтому также можно сделать вывод, что после включения escape-анализа, если это не метод escape, то объект размещается в стеке.

синхронизировать пропустить

При динамической компиляции синхронизированных блоков JIT-компилятор может использовать escape-анализ, чтобы определить, может ли объект блокировки, используемый синхронизированным блоком, быть доступен только одному потоку и не может быть освобожден для других потоков.

Если объект блокировки, используемый синхронизированным блоком, окажется доступным только для одного потока посредством этого анализа, то JIT-компилятор отменит синхронизацию этой части кода при компиляции синхронизированного блока Вызов снятия блокировки.

скалярная замена

Когда с помощью escape-анализа определяется, что доступ к объекту извне невозможен и объект может быть подвергнут дальнейшей декомпозиции, JVM не будет создавать объект, а разложит переменные-члены объекта на несколько переменных-членов, используемых этим методом. пространство в кадре стека или регистре, чтобы не было выделения памяти объекта из-за отсутствия большого непрерывного пространства. Включить скалярные параметры подстановки-XX:+EliminateAllocations, он включен по умолчанию после JDK7

Скаляр и агрегат

Скаляр — это величина, которая не может быть далее разложена, а базовый тип данных Java — это скаляр (например, int, long и другие базовые типы данных и ссылочные типы и т. д.), противоположность скаляру — это величина, которая может подвергаться дальнейшему разложению, и эта величина называется совокупной суммой. В Java объекты — это агрегаты, которые можно дополнительно разбить.

На этапе JIT, если обнаружено, что объект не доступен для внешнего мира после анализа побега, то после JIT-оптимизации объект будет разобран на несколько переменных-членов, содержащих несколько переменных-членов для замены.

public static void main(String[] args) {
   alloc();
}

private static void alloc() {
   Point point = new Point(1,2);
   System.out.println("point.x="+point.x+"; point.y="+point.y);
}
class Point{
    private int x;
    private int y;
}

В приведенном выше коде точечный объект не экранируется.allocметод, а точечный объект можно разобрать на скаляры. Тогда JIT не будет напрямую создавать объект Point, а напрямую будет использовать два скаляра int x, int y для замены объекта Point.

После замены:

private static void alloc() {
   int x = 1;
   int y = 2;
   System.out.println("point.x="+x+"; point.y="+y);
}

Эта замена может значительно сократить использование памяти кучи, потому что если объект не нужно создавать, то нет необходимости и выделять память кучи.