Как реализуются внутренние интерфейсы Java? (виртуальные таблицы?)

С++ имеет множественное наследование. Реализация множественного наследования на уровне сборки может быть довольно сложной, но есть хорошие описания в Интернете о том, как это обычно делается (vtables, pointer исправления, трюки и т.д.).

Java не имеет множественного наследования реализации, но имеет наследование нескольких интерфейсов, поэтому я не думаю, что реализация с прямой версией с одним vtable для каждого класса может реализовать это. Как Java реализует интерфейсы внутри?

Я понимаю, что, вопреки С++, Java является компиляцией Jit, поэтому различные фрагменты кода могут быть оптимизированы по-разному, и разные JVM могут делать что-то по-другому. Итак, существует ли какая-то общая стратегия, которую придерживаются многие JVM, или кто-либо знает реализацию в конкретной JVM?

Также JVM часто девиртуализуют и строят вызовы методов, и в этом случае нет каких-либо подключаемых vtables или эквивалентов, поэтому может не иметь смысла спрашивать о фактических последовательностях сборки, которые реализуют вызовы методов виртуального/интерфейса, но я предполагаю, что большинство JVM все еще сохраняют какое-то общее представление классов вокруг, чтобы использовать, если они не смогли полностью девиртуализировать. Это предположение неверно? Такое представление выглядит так, как С++ vtable? Если это так, интерфейсы имеют отдельные vtables и как они связаны с классами vtables? Если это так, экземпляры экземпляров могут иметь несколько указателей vtable (для vtables класса/интерфейса), например, экземпляры объектов на С++? Связаны ли ссылки типа класса и типа интерфейса с одним и тем же объектом одинаковое двоичное значение или могут ли они отличаться, как на С++, где им требуются исправления указателя?

(для справки: этот вопрос спрашивает что-то подобное о CLR, и, как представляется, это хорошее объяснение в эта статья msdn, хотя это может быть устаревшей к настоящему времени. Я не смог найти ничего подобного для Java.)

Edit:

  • Я имею в виду "реализует" в смысле "Как компилятор GCC реализует целые добавления/функции вызовов /etc ", а не в смысле "Java-класс ArrayList реализует интерфейс List".
  • Я знаю, как это работает на уровне байт-кода JVM, что я хочу знать, это то, какой код и структуры данных генерируются JVM после того, как он завершил загрузку файлов классов и компиляцию байт-кода.

Ответ 1

Ключевой особенностью JVM HotSpot является встроенное кэширование. Это на самом деле не означает, что целевой метод встроен, но означает, что предположение помещается в JIT-код, который каждый будущий вызов виртуальному или интерфейсу будет нацелен та же самая реализация (то есть, что сайт вызова является мономорфным). В этом случае проверка компилируется в машинный код, действительно ли предположение выполняется (т.е. тип целевого объекта такой же, как и в прошлый раз), а затем управление передачей непосредственно к целевому методу - без каких-либо виртуальных таблиц. Если утверждение не удастся, может быть предпринята попытка преобразовать его в сайт мегамоморфного вызова (т.е. С несколькими возможными типами); если это также не удается (или если это первый вызов), выполняется регулярный поиск с длинным ветром, используя vtables (для виртуальных методов) и itables (для интерфейсов).

Изменить. Hotspot Wiki содержит более подробную информацию о vtable и itable stub. В полиморфном случае он по-прежнему помещает встроенную версию кеша в сайт вызова. Тем не менее, код на самом деле является заглушкой, которая выполняет поиск в таблице vtable или в режиме itable. Для каждого смещения vtable есть один столбец vtable (0, 1, 2,...). Интерфейсные вызовы добавьте линейный поиск по массиву итабелей, прежде чем искать в itable (если найдено) при заданном смещении.