Почему определение класса как финального улучшает производительность JVM?
Цитата из http://sites.google.com/site/gson/gson-design-document:
Почему большинство классов в Gson помечены как окончательные?
В то время как Gson предоставляет довольно расширяемую архитектуру, предоставляя подключаемые сериализаторы и десериализаторы, классы Gson не были специально разработаны для расширения. Предоставление не финальных классов позволило бы пользователю законно расширять классы Gson, а затем ожидать, что такое поведение будет работать во всех последующих ревизиях. Мы решили ограничить такие варианты использования, помечая классы как final и ожидая, пока не появится хороший сценарий использования для обеспечения расширяемости. Пометка финала класса также имеет незначительное преимущество, предоставляя дополнительные возможности оптимизации для компилятора Java и виртуальной машины.
Почему это так? [Если бы я догадался: JVM знает, что класс является окончательным, он не поддерживает таблицы переопределения методов? Есть ли другие причины?]
Какое преимущество в производительности?
Относится ли это к классам с частотой создания экземпляров (POJO?) Или, возможно, к классам, являющимся держателями статических методов (служебные классы)?
Методы, определенные как окончательные, также могут теоретически улучшить производительность?
Есть ли какие-либо последствия?
Спасибо, Максим.
5 ответов
Виртуальные (переопределенные) методы обычно реализуются с помощью некоторой таблицы (vtable), которая в конечном итоге является указателем на функцию. У каждого вызова метода есть издержки, связанные с этим указателем. Когда классы помечены как окончательные, все методы не могут быть переопределены, и использование таблицы больше не требуется - это быстрее.
Некоторые виртуальные машины (например, HotSpot) могут действовать более разумно и знать, когда методы не переопределены, и генерировать более быстрый код в зависимости от ситуации.
Вот более конкретная информация о HotSpot. И некоторая общая информация тоже.
Старая, по-видимому, больше не актуальная статья об IBM developerWorks, в которой говорится:
Распространенное мнение состоит в том, что объявление классов или методов как финальных облегчает компилятору встроенные вызовы методов, но это восприятие неверно (или, по крайней мере, сильно завышено).
Финальные классы и методы могут создавать значительные неудобства при программировании - они ограничивают ваши возможности повторного использования существующего кода и расширения функциональности существующих классов. Хотя иногда класс делается окончательным по уважительной причине, например, для обеспечения неизменности, преимущества использования final должны перевешивать неудобства. Повышение производительности почти всегда является плохой причиной для компромисса с хорошими принципами объектно-ориентированного проектирования, и когда повышение производительности является небольшим или отсутствует, это действительно плохой компромисс.
Также посмотрите этот связанный ответ на другой вопрос. Есть также эквивалентный вопрос для.Net, обсуждаемый здесь. ТАКАЯ дискуссия " Заключены ли окончательные методы?" На вопрос " Какие оптимизации будут бесполезны завтра", этот вопрос появляется в списке.
Вот кто-то пытается охарактеризовать влияние на встраивание HotSpot от статических и конечных классов.
Отметим также, что существует эффект запутывания final
классы против final
методы. Вы можете получить некоторое преимущество в производительности (опять же, у меня нет хороших рекомендаций) для final
методы наверняка, так как он может заставить JIT делать встраивание, он не может иначе (или не так просто). Вы получаете тот же эффект, когда вы отмечаете класс final
, что означает, что все методы также внезапно становятся окончательными. Обратите внимание, что люди Sun/Oracle утверждают, что HotSpot обычно может делать это с или без final
ключевое слово. Есть ли какие-либо дополнительные эффекты от наличия самого класса final
?
Для справки, ссылки на JLS о финальных методах и финальных классах.
Не зная реализации каждой конкретной JVM, я бы теоретически сказал, что если JVM знает, что указатель на объект является указателем на тип, который является окончательным, он может выполнять не виртуальные вызовы функций (то есть прямые или косвенные) к функциям-членам (т. е. отсутствие косвенного обращения через указатель на функцию), что может привести к более быстрому выполнению. Это может также в свою очередь привести к возможностям встраивания.
Маркировка классов как окончательных позволяет применять дополнительную оптимизацию на этапе JIT.
Если вы вызываете виртуальный метод для неконечного класса, вы не знаете, является ли правильная реализация той, которая определена в этом классе, или какой-то подкласс, о котором вы не знаете.
Однако, если у вас есть ссылка на последний класс, вы знаете конкретную реализацию, которая требуется.
Рассматривать:
A extends B
B extends C
B myInstance = null;
if(someCondition)
myInstance = new B();
else
myInstance = new C();
myInstance.toString();
В этом случае JIT не может знать, будет ли вызвана реализация toString() в C или B реализована в toString(). Однако, если B помечен как окончательный, невозможно, чтобы любая реализация, кроме B, была правильной реализацией.
Нет разницы, это просто предположение. Единственная ситуация, в которой это имеет смысл - это классы типа String и т. Д., Где jvm относится к ним по-разному.