Улучшает ли производительность использование ключевого слова final в Java?
В Java мы видим множество мест, где может использоваться ключевое слово final
, но его использование встречается редко.
Например:
String str = "abc";
System.out.println(str);
В приведенном выше случае str
может быть final
, но это обычно не учитывается.
Когда метод никогда не будет переопределен, мы можем использовать ключевое слово final . Аналогично в случае класса, который не будет наследоваться.
Действительно ли использование ключевого слова final в любом или во всех этих случаях повышает производительность? Если да, то каким образом? Пожалуйста, объясните. Если правильное использование final
действительно имеет значение для производительности, какие привычки должен выработать Java-программист, чтобы наилучшим образом использовать ключевое слово?
Переведено автоматически
Ответ 1
Обычно нет. Для виртуальных методов HotSpot отслеживает, был ли метод фактически переопределен, и может выполнять оптимизации, такие как встраивание, исходя из предположения, что метод не был переопределен - до тех пор, пока не загрузит класс, который переопределяет метод, после чего он может отменить (или частично отменить) эти оптимизации.
(Конечно, это предполагает, что вы используете HotSpot - но это, безусловно, самая распространенная JVM, так что ...)
На мой взгляд, вы должны использовать final
исходя из понятного дизайна и удобочитаемости, а не из соображений производительности. Если вы хотите что-либо изменить из соображений производительности, вам следует выполнить соответствующие измерения, прежде чем искажать наиболее четкий код - таким образом, вы сможете решить, стоит ли достигнутая дополнительная производительность ухудшения читаемости / дизайна. (По моему опыту, оно того почти никогда не стоит; YMMV.)
РЕДАКТИРОВАТЬ: Поскольку уже упоминались поля final , стоит упомянуть, что они в любом случае часто являются хорошей идеей с точки зрения четкого дизайна. Они также изменяют гарантированное поведение с точки зрения видимости между потоками: после завершения работы конструктора любые поля final гарантированно будут немедленно видны в других потоках. Это, вероятно, наиболее распространенное использование final
по моему опыту, хотя, как сторонник эмпирического правила Джоша Блоха "разработай наследование или запрети его", мне, вероятно, следует использовать final
чаще для классов...
Ответ 2
Краткий ответ: не беспокойтесь об этом!
Длинный ответ:
Говоря о конечных локальных переменных, имейте в виду, что использование ключевого слова 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 1.7.0_45-b18.
Итак, насколько реально улучшается производительность? Я не осмеливаюсь сказать. В большинстве случаев, вероятно, незначительно (~ 270 наносекунд в этом синтетическом тесте, потому что конкатенация строк полностью исключена - редкий случай), но в высокооптимизированном служебном коде это может быть фактором. В любом случае ответ на исходный вопрос - да, это может улучшить производительность, но в лучшем случае незначительно.
Помимо преимуществ во время компиляции, я не смог найти никаких доказательств того, что использование ключевого слова final
оказывает какое-либо измеримое влияние на производительность.
Ответ 3
ДА, может. Вот пример, когда final может повысить производительность:
Conditional compilation is a technique in which lines of code are not compiled into the class file based on a particular condition. This can be used to remove tons of debugging code in a production build.
consider the following:
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.
}
}
By converting the doSomething attribute into a final attribute, you have told the compiler that whenever it sees doSomething, it should replace it with false as per the compile-time substitution rules. The first pass of the compiler changes the code to something like this:
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."<init>":()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
.
Это доказывает, что конечные переменные влияют на производительность, по крайней мере, в этом простом случае.
Ответ 4
Согласно IBM - это не относится к классам или методам.
http://www.ibm.com/developerworks/java/library/j-jtp04223.html