Разговор о скалярной замене в jvm

Java

Обзор

Обычно для создания объекта в java все думают, что он создается в куче. В jdk6 есть такие технологии, как escape-анализ и скалярная замена, и создание объектов в куче уже не является абсолютным.

Что касается скалярной замены, обзор дается следующими пунктами:

  1. анализ побега
  2. Что такое скалярная замена
  3. тестовая скалярная замена

анализ побега

Escape-анализ — это метод анализа, который анализирует динамическую область объекта, чтобы обеспечить основу для других мер по оптимизации. Например, анализ объекта не будет ускользать за пределы метода или за пределы потока, другие меры оптимизации (распределение стека, скалярная замена и т. д.) оптимизируются в соответствии со степенью ускользания.

Пример анализа побега
public class EscapeAnalysis {
    public Person p;
    /**
     * 发生逃逸,对象被返回到方法作用域以外,被方法外部,线程外部都可以访问
     */
    public void escape(){
        p = new Person(26, "TomCoding escape");
    }

    /**
     * 不会逃逸,对象在方法内部
     */
    public String noEscape(){
        Person person = new Person(26, "TomCoding noEscape");
        return person.name;
    }
}

static class Person {
    public int age;
    public String name;
    
    ... // 省略构造方法
}

Что такое скалярная замена

Скаляр можно понимать как неразложимую переменную, такую ​​как базовые типы данных и ссылочные типы в java. Соответствующие агрегаты могут быть разобраны, например объекты.

Когда анализ объекта с помощью escape действует только внутри метода, виртуальная машина может оптимизировать его, используя скалярную подстановку.

Например, в приведенном выше методе noEscape() объект person будет находиться только внутри метода, а следующий псевдокод будет получен с помощью метода скалярной замены:

/**
 * 不会逃逸,对象在方法内部
 */
public String noEscape(){
    int age = 26;
    String name = "TomCoding noEscape";
    return name;
}

Видно, что после запуска приведенного выше псевдокода объект Person не будет создаваться путем выделения памяти из кучи, а будет декомпозировать переменные-члены как локальные переменные в кадре стека. Таким образом, кадр стека будет уничтожен по завершении вызова метода. Эффективно уменьшить количество объектов, созданных в куче, и количество сборщиков мусора.

тестовая скалярная замена

Далее мы тестируем метод noEscape(), в основном тестируя два сценария:

  1. Не используйте скалярную замену
  2. Используйте скалярную замену

Следующие тесты выполняются в jdk8 (обратите внимание, что jdk8 по умолчанию включает анализ побега и технологию скалярной замены)

Код теста выглядит следующим образом:

void testEliminateAllocationsWithNoEscape() {
    int n = 100000000;
    long start = System.currentTimeMillis();
    EscapeAnalysis escapeAnalysis = new EscapeAnalysis();
    for (int i = 0; i < n; i++) {
        // noEscape()不会发生逃逸
        escapeAnalysis.noEscape();
    }
    System.out.println("耗时:" + (System.currentTimeMillis() - start));
}

Не используйте скалярную замену

Установите параметры jvm следующим образом:

-Xms5m  最小堆内存5M
-Xmx5m  最大堆内存5M
-XX:+PrintGC    打印gc日志
-XX:-EliminateAllocations 关闭标量替换优化

Затраты времени на моем локальном компьютере после запуска: 3006 миллисекунд, gc выполняется более 2000 раз.

Используйте скалярную замену

Установите параметры jvm следующим образом:

-Xms5m  最小堆内存5M
-Xmx5m  最大堆内存5M
-XX:+PrintGC    打印gc日志
-XX:+EliminateAllocations 关闭标量替换优化

Занимает много времени на моей машине после запуска: 20 мс, gc происходит 6 раз.

Давайте посмотрим на скалярный эффект замены сбежавшего объекта.

Код теста выглядит следующим образом:

void testEliminateAllocationsWithEscape() {
    int n = 100000000;
    long start = System.currentTimeMillis();
    EscapeAnalysis escapeAnalysis = new EscapeAnalysis();
    for (int i = 0; i < n; i++) {
        // escape()发生逃逸
        escapeAnalysis.escape();
    }
    System.out.println("耗时:" + (System.currentTimeMillis() - start));
}

Установите параметры jvm следующим образом:

-Xms5m  最小堆内存5M
-Xmx5m  最大堆内存5M
-XX:+PrintGC    打印gc日志
-XX:+EliminateAllocations 关闭标量替换优化

Затраты времени на моем локальном компьютере после запуска: 3705 миллисекунд, gc выполняется более 2000 раз.

Суммировать

  1. Можно видеть, что количество gcs (уменьшенное количество объектов, созданных в куче) эффективно уменьшается с помощью анализа побега и методов скалярной замены.
  2. Это идеальная ситуация, чтобы избежать ситуаций выхода объекта во время фактического кодирования. Осведомленность о кодировании может быть сформирована, чтобы свести к минимуму побег объекта.

думать

Скалярная замена — это лишь одна из мер оптимизации с помощью escape-анализа.Есть ли другие меры оптимизации?