Улучшает ли производительность ключевое слово final в Java?

В Java мы видим множество мест, где можно использовать final ключевое слово, но его использование является необычным.

Например:

 String str = "abc"; System.out.println(str); 

В приведенном выше случае str может быть final но это обычно останавливается.

Когда метод никогда не будет переопределен, мы можем использовать ключевое слово final. Аналогично в случае classа, который не будет наследоваться.

Использует ли последнее ключевое слово в любом или всех этих случаях действительно повысить производительность? Если да, то как? Пожалуйста, объясни. Если правильное использование final действительно важно для производительности, какие навыки должен разрабатывать Java-программист, чтобы наилучшим образом использовать ключевое слово?

Обычно нет. Для виртуальных методов HotSpot отслеживает, действительно ли метод был переопределен, и может выполнять такие оптимизации, как inline, при условии, что метод не был переопределен – пока он не загрузит class, который переопределяет метод, и в этот момент он может отменить (или частично отменить) эти оптимизации.

(Конечно, предполагается, что вы используете HotSpot, но это, безусловно, самый распространенный JVM, поэтому …)

На мой взгляд, вы должны использовать final на основе четкого дизайна и удобочитаемости, а не по соображениям производительности. Если вы хотите изменить что-либо по соображениям производительности, вы должны выполнить соответствующие измерения, прежде чем изгибать самый чистый код вне формы – таким образом вы сможете решить, стоит ли достижение какой-либо дополнительной производительности более плохой читаемости / дизайна. (По моему опыту это почти никогда не стоит того, YMMV.)

EDIT: Как уже упоминалось в заключительных областях, стоит обратить внимание на то, что они часто являются хорошей идеей, в плане четкого дизайна. Они также изменяют гарантированное поведение с точки зрения видимости поперечного streamа: после завершения конструктора любые конечные поля гарантированно будут видны в других streamах немедленно. Это, вероятно, самое распространенное использование final в моем опыте, хотя, как сторонник эмпирического эмпирического правила «дизайн для наследования или запрещения» Джоша Блоха, я, вероятно, должен чаще использовать final для classов …

Короткий ответ: не беспокойтесь об этом!

Длительный ответ:

Когда речь идет о конечных локальных переменных, имейте в виду, что использование ключевого слова final поможет компилятору оптимизировать код статически , что в конечном итоге может привести к более быстрому коду. Например, окончательные строки a + b в приведенном ниже примере объединены статически (во время компиляции).

 public class FinalTest { public static final int N_ITERATIONS = 1000000; public static String testFinal() { final String a = "a"; final String b = "b"; return a + b; } public static String testNonFinal() { String a = "a"; String b = "b"; return a + b; } public static void main(String[] args) { long tStart, tElapsed; tStart = System.currentTimeMillis(); for (int i = 0; i < N_ITERATIONS; i++) testFinal(); tElapsed = System.currentTimeMillis() - tStart; System.out.println("Method with finals took " + tElapsed + " ms"); tStart = System.currentTimeMillis(); for (int i = 0; i < N_ITERATIONS; i++) testNonFinal(); tElapsed = System.currentTimeMillis() - tStart; System.out.println("Method without finals took " + tElapsed + " ms"); } } 

Результат?

 Method with finals took 5 ms Method without finals took 273 ms 

Протестировано на Java Hotspot VM 1.7.0_45-b18.

Итак, каково фактическое улучшение производительности? Я не смею сказать. В большинстве случаев, вероятно, маргинальные (~ 270 наносекунд в этом синтетическом тесте, потому что конкатенация строк вообще исключается - редкий случай), но в высоко оптимизированном коде полезности это может быть фактором. В любом случае ответ на исходный вопрос да, это может улучшить производительность, но в лучшем случае незначительно .

Компиляционные преимущества в стороне, я не мог найти никаких доказательств того, что использование ключевого слова final оказывает какое-либо измеримое влияние на производительность.

Да, оно может. Вот пример, где final может повысить производительность:

Условная компиляция – это метод, при котором строки кода не компилируются в файл classа на основе определенного условия. Это можно использовать для удаления тонны кода отладки в сборке.

рассмотрите следующее:

 public class ConditionalCompile { private final static boolean doSomething= false; if (doSomething) { // do first part. } if (doSomething) { // do second part. } if (doSomething) { // do third part. } if (doSomething) { // do finalization part. } } 

Преобразуя атрибут doSomething в конечный атрибут, вы сказали компилятору, что всякий раз, когда он видит doSomething, он должен заменить его на false в соответствии с правилами замены времени компиляции. Первый проход компилятора изменяет код на что-то вроде этого:

 public class ConditionalCompile { private final static boolean doSomething= false; if (false){ // do first part. } if (false){ // do second part. } if (false){ // do third part. } if (false){ // do finalization part. } } 

Как только это будет сделано, компилятор еще раз посмотрит на него и увидит, что в коде есть недостижимые утверждения. Поскольку вы работаете с компилятором высшего качества, ему не нравятся все эти недостижимые байтовые коды. Поэтому он удаляет их, и в итоге вы получаете следующее:

 public class ConditionalCompile { private final static boolean doSomething= false; public static void someMethodBetter( ) { // do first part. // do second part. // do third part. // do finalization part. } } 

таким образом уменьшая любые чрезмерные коды или любую ненужную условную проверку.

Изменить: В качестве примера возьмем следующий код:

 public class Test { public static final void main(String[] args) { boolean x = false; if (x) { System.out.println("x"); } final boolean y = false; if (y) { System.out.println("y"); } if (false) { System.out.println("z"); } } } 

При компиляции этого кода с Java 8 и декомпиляции с помощью javap -c Test.class мы получаем:

 public class Test { public Test(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."":()V 4: return public static final void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: ifeq 14 6: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #22 // String x 11: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: iconst_0 15: istore_2 16: return } 

Мы можем отметить, что скомпилированный код включает в себя только не конечную переменную x . Это предполагает, что конечные переменные влияют на производительность, по крайней мере, для этого простого случая.

По словам IBM – это не для classов или методов.

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

Вы действительно спрашиваете о двух (по крайней мере) разных случаях:

  1. final для локальных переменных
  2. final для методов / classов

Джон Скит уже ответил 2). Около 1):

Я не думаю, что это имеет значение; для локальных переменных компилятор может определить, является ли эта переменная окончательной или нет (просто проверяя, назначена ли она более одного раза). Поэтому, если компилятор хочет оптимизировать переменные, которые назначаются только один раз, он может сделать это независимо от того, действительно ли переменная объявлена final или нет.

final может иметь значение для полей protected / public class; компилятору очень сложно выяснить, установлено ли поле более одного раза, поскольку это может произойти из другого classа (который, возможно, даже не был загружен). Но даже тогда JVM может использовать технику, которую описывает Джон (оптимизируйте оптимистично, верните, если class загружен, который меняет поле).

Таким образом, я не вижу причин, по которым это должно помочь производительности. Таким образом, такая микро-оптимизация вряд ли поможет. Вы можете попробовать сравнить его, чтобы убедиться, но я сомневаюсь, что это изменит ситуацию.

Редактировать:

Фактически, согласно ответам Тимо Уэсткемпера, final может улучшить производительность для полей classов в некоторых случаях. Я стою исправлено.

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

Для справки это было протестировано против javac версии 8 , 9 и 10 .

Предположим, что этот метод:

 public static int test() { /* final */ Object left = new Object(); Object right = new Object(); return left.hashCode() + right.hashCode(); } 

Компилируя этот код так, как он есть, выдает тот же самый байт-код, что и в случае, если final был бы доступен ( final Object left = new Object(); ).

Но это одно:

 public static int test() { /* final */ int left = 11; int right = 12; return left + right; } 

Производит:

  0: bipush 11 2: istore_0 3: bipush 12 5: istore_1 6: iload_0 7: iload_1 8: iadd 9: ireturn 

Выход из final который должен присутствовать, производит:

  0: bipush 12 2: istore_1 3: bipush 11 5: iload_1 6: iadd 7: ireturn 

Код в значительной степени не требует пояснений, если существует постоянная времени компиляции, он будет загружен непосредственно в стек операнда (он не будет храниться в массиве локальных переменных, как в предыдущем примере, через bipush 12; istore_0; iload_0 ) – какой смысл имеет смысл, поскольку никто не может его изменить.

С другой стороны, почему во втором случае компилятор не производит istore_0 ... iload_0 находится вне меня, это не похоже на то, что слот 0 используется каким-либо образом (он может сжимать массив переменных таким образом, но может быть Im, внутренние детали, не могу точно сказать)

Я был удивлен, увидев такую ​​оптимизацию, учитывая, как мало что делает javac . А мы должны всегда использовать final ? Я даже не собираюсь писать JMH тест (который я хотел первоначально), я уверен, что diff находится в порядке ns (если возможно вообще быть захвачен). Единственное место, где это может быть проблемой, – это когда метод не может быть встроен из-за его размера (и объявление final уменьшит этот размер на несколько байтов).

Есть еще два final вопроса, которые необходимо решить. Во-первых, когда метод является final (с точки зрения JIT ), такой метод monoморфен – и это самые любимые JVM .

Тогда есть final переменные экземпляра (которые должны быть установлены в каждом конструкторе); они важны, поскольку они гарантируют корректно опубликованную ссылку, как это немного коснулось здесь, а также точно указано JLS .

Примечание. Не эксперт по Java

Если я правильно помню свою Java-версию, было бы очень мало способов повысить производительность, используя ключевое слово final. Я всегда знал, что он существует для «хорошего кода» – дизайна и удобочитаемости.

Я не эксперт, но я полагаю, вы должны добавить final ключевое слово к classу или методу, если оно не будет перезаписано и оставить переменные в одиночку. Если будет какой-то способ оптимизировать такие вещи, компилятор сделает это за вас.

Фактически, тестируя некоторый код, связанный с OpenGL, я обнаружил, что использование последнего модификатора в частном поле может ухудшить производительность. Вот начало тестирования classа I:

 public class ShaderInput { private /* final */ float[] input; private /* final */ int[] strides; public ShaderInput() { this.input = new float[10]; this.strides = new int[] { 0, 4, 8 }; } public ShaderInput x(int stride, float val) { input[strides[stride] + 0] = val; return this; } // more stuff ... 

И это метод, который я использовал для проверки производительности различных альтернатив, среди которых class ShaderInput:

 public static void test4() { int arraySize = 10; float[] fb = new float[arraySize]; for (int i = 0; i < arraySize; i++) { fb[i] = random.nextFloat(); } int times = 1000000000; for (int i = 0; i < 10; ++i) { floatVectorTest(times, fb); arrayCopyTest(times, fb); shaderInputTest(times, fb); directFloatArrayTest(times, fb); System.out.println(); System.gc(); } } 

После третьей итерации, когда VM разогрелась, я последовательно получил эти цифры без окончательного ключевого слова:

 Simple array copy took : 02.64 System.arrayCopy took : 03.20 ShaderInput took : 00.77 Unsafe float array took : 05.47 

С ключевым словом final:

 Simple array copy took : 02.66 System.arrayCopy took : 03.20 ShaderInput took : 02.59 Unsafe float array took : 06.24 

Обратите внимание на цифры для теста ShaderInput.

Не имело значения, сделал ли я поля публичными или частными.

Кстати, есть еще несколько озадачивающих вещей. Класс ShaderInput превосходит все остальные варианты, даже с ключевым словом final. Это замечательный b / c, в основном это class, который обертывает массив float, в то время как другие тесты напрямую управляют массивом. Придется это понять. Может иметь какое-то отношение к свободному интерфейсу ShaderInput.

Также System.arrayCopy на самом деле, по-видимому, несколько медленнее для небольших массивов, чем просто копирование элементов из одного массива в другой в цикле for. И использование sun.misc.Unsafe (а также прямой java.nio.FloatBuffer, не показанный здесь) выполняется ужасно.

Определенно да, если переменная преобразуется как постоянная,

поскольку мы знаем, что java-компилятор преобразует такие конечные переменные, как константа, что возможно

поскольку концепция постоянного java-компилятора напрямую заменяет значение ссылкой на время компиляции

в java-переменных становится постоянным, если это строковый или примитивный тип, который задан без какого-либо процесса выполнения

в противном случае это просто конечная (non changeble) переменная,

& constatnt всегда быстрее, чем ссылка.

поэтому, если это возможно, используйте константы на любом языке программирования для повышения производительности

Участники, объявленные с окончательной версией, будут доступны по всей программе, потому что, в отличие от нефинальных, если эти участники не использовались в программе, все равно они не будут заботиться о сборщике мусора, поэтому могут возникнуть проблемы с производительностью из-за плохого управления памятью.

ключевое слово final может использоваться пятью способами в Java.

  1. Класс является окончательным
  2. Контрольная переменная является конечной
  3. Локальная переменная является конечной
  4. Метод окончательный

Класс является окончательным: class является окончательным, мы не можем продлить или наследование означает, что наследование невозможно.

Аналогично – объект является окончательным: некоторое время мы не изменяем внутреннее состояние объекта, поэтому в таком случае мы можем указать, что объект является конечным объектом. Объект final означает, что переменная не является окончательной.

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

  • Почему Stream не реализует Iterable ?
  • Вывод из цикла for в Java
  • Почему autoboxing не отменяет varargs при использовании перегрузки методов в Java 7?
  • Java - Использование методов Accessor и Mutator
  • Регулярное выражение для соответствия подстроке, за которой не следует определенная другая подстрока
  • Использование ThreadFactory в Java
  • Что означает ключевое слово «переходный» в Java?
  • Зачем использовать polymorphism?
  • Eclipse сообщила: «Не удалось загрузить общую библиотеку JNI»
  • Почему отсутствующая аннотация не вызывает исключение ClassNotFoundException во время выполнения?
  • Почему я получаю ошибку «401 Unauthorized» в Maven?
  • Interesting Posts

    Как перенести приложение на фронт каждые 15 минут на Mac OS X Lion?

    Перегрузка и переопределение Java

    Windows 7 VPN останавливает веб-браузер

    Прикрепите источник в затмении фляги

    Как сообщить интерпретатору R, как использовать прокси-сервер?

    Структура хромовой ОС

    Несколько приложений Spark с HiveContext

    Можем ли мы пригласить людей использовать наше приложение или отправить запрос друга из приложения через Facebook в iOS 5?

    Как установить цвет текста TextView в коде?

    Как я могу исправить батарею ноутбука, которая не будет полностью заряжаться?

    Как установить представление списка в качестве представления по умолчанию FTP в проводнике Windows?

    Как получить аргументы vm из java-приложения?

    Отображение карты Google в модальном режиме, созданном с помощью Twitter-бутстрапа

    Системное заикание, вызванное жестким диском

    Служба имеет нулевые приложения (неинфраструктурные) конечные точки

    Давайте будем гением компьютера.