Как сборщик мусора обновляет ссылки, помещенные в стек операндов? - PullRequest
2 голосов
/ 03 марта 2020

JVM может легко обновлять ссылки на локальные переменные, stati c ссылки, экземпляры классов или массивы объектов при перемещении объекта в куче. Но как он может обновить ссылки, помещенные в стек операндов?

1 Ответ

8 голосов
/ 03 марта 2020

Нет принципиальной разницы между локальной переменной и записью в стеке операндов. Оба живут в одном кадре стека. Ни один из них официально не объявлен, и обеим требуется, чтобы JVM выполнила логический вывод, чтобы распознать их фактическое использование.

Следующий код

public static void example() {
    {
        int foo = 42;
    }
    {
        Object bar = "text";
    }
    {
        long x = 100L;
    }
    {
        Object foo, bar = new Object();
    }
}

(обычно) будет скомпилирован в

  public static void example();
    Code:
       0: bipush        42
       2: istore_0
       3: ldc           #1                  // String text
       5: astore_0
       6: ldc2_w        #2                  // long 100l
       9: lstore_0
      10: new           #4                  // class java/lang/Object
      13: dup
      14: invokespecial #5                  // Method java/lang/Object."<init>":()V
      17: astore_1
      18: return

Обратите внимание, как локальная переменная с индексом 0 в кадре стека переназначается со значениями разных типов. В качестве бонуса последнее значение переменной индекса 1 делает недействительной переменную с индексом 0, поскольку в противном случае она будет содержать висячую половину значения long.

Никаких дополнительных указаний относительно типа Для локальных переменных отладочная информация является необязательной, и таблицы стековых карт существуют только в том случае, если код содержит ветви.

Единственный способ определить, содержит ли локальная переменная ссылку, состоит в том, чтобы следить за ходом программы и прослеживать эффект из инструкций. Это уже подразумевает вывод значений в стеке операндов, так как без него мы бы даже не знали, что инструкция store помещает в переменную.

Это делает верификатор, это даже обязательно, и сборщик мусора или любой другой поддерживающий код JVM тоже может это сделать. Реализация может даже иметь один анализирующий код, хранящий информацию о типе первого анализа, которая будет проверкой.

Но даже когда эта информация восстанавливается каждый раз, когда это требуется сборщику мусора, накладные расходы не будут астрономический. Сборщик мусора запускается только периодически, и ему нужна только эта информация для выполняемых в данный момент методов. И это только интерпретированное выполнение.

Когда JIT-компилятор генерирует код, ему все равно нужно использовать информацию о типе и можно подготовить информацию для сборщика мусора, но это будет сделано только для определенных точек, называемых * 1024. * safepoints , где сгенерированный код проверяет, есть ли ожидающая сборка мусора. Это означает, что между этими точками данные не обязательно должны быть в форме, понятной сборщику мусора, и оптимизированный код может предполагать, что сборщик мусора не будет перемещать объекты во время их обработки.

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

...