Есть ли причина для того, чтобы не вызывать виртуальную инструкцию и команду invokeinteface bytecode в одну?
Есть ли причина для создания инструкции для вызова нестатического неконструктивного метода в две отдельные команды вместо одной объединенной инструкции, например invokeinstance
? Это имеет какое-то отношение к какому-то случайному внутреннему механизму JVM, или это еще одна ужасная проблема наследия?
Я знаю у нас invokespecial
потому что вызывающий конструктор требует проверки имени, маркировка еще одного конструктора была выполнена и т. д., и invokestatic
потому что нам не нужен объектный реф, сбрасываемый в новый фрейм стека. Однако у него нет простой причины, по которой Sun предпочитает, чтобы в нее вливались возможные универсальные инструкции. invokevirtual
а также invokeinterface
, Без разделения код ASM может быть намного проще, так как нам не нужно просматривать все суперинтерфейсы, чтобы увидеть, является ли это интерфейсным методом, создающим сложность кода.
1 ответ
Invokeinterface отличается тем, что интерфейсы проверяются только во время выполнения. С помощью виртуального метода вы можете статически определить, что тип является подтипом класса, в котором определен метод. Для интерфейса невозможно точно знать, имеет ли значение тип, реализующий этот интерфейс, не зная тип значения времени выполнения.
Рассмотрим следующий псевдокод (обратите внимание, что это не разрешено в Java, но эквивалент байт-кода разрешен JVM)
class A
class B extends A implements Foo
A a = new B()
a.fooMethod()
Нет никакого способа статически узнать, реализует ли Foo объект A или нет, потому что статический тип A не реализует Foo, а фактический тип времени выполнения B делает.
Изменить: приведенный выше пример будет отклонен компилятором Java, но не JVM. Вы можете удивиться, почему JVM не применяет те же правила, что и компилятор. Разница в том, что JVM не имеет информации о типе источника на уровне локальных переменных. Рассмотрим следующий пример, который разрешен в Java.
class A
class B extends A implements Foo
class C extends A implements Foo
Foo x = null;
if (whatever) {
x = new B();
} else {
x = new C();
}
x.fooMethod();
JVM не знает предполагаемый тип x (без стековых допустимых значений, которые не были представлены намного позже), поэтому выводит тип x как A
, который не реализует Foo
, Поэтому, если бы он попытался статически проверить интерфейсы как время проверки, он отклонил бы действительный код Java! Единственное возможное решение - не проверять интерфейсы.
Для безопасной проверки интерфейсов JVM должна иметь возможность выводить типы, такие как "подкласс A, который также реализует Foo", что, очевидно, добавляет огромную сложность чему-то, что должно быть быстрым и эффективным. Поэтому имеет смысл, что дизайнеры не пошли по этому пути.
PS Invokespecial не только для конструкторов - он также используется для частных и супер вызовов методов. Скорее всего, изначально это была отдельная инструкция в качестве оптимизации, поскольку вызываемый метод известен во время загрузки, а не изменяется в зависимости от типа времени выполнения цели. На самом деле его изначально называли invokenonvirtual.