Cost of using final fields
Asked Answered
A

1

20

We know that making fields final is usually a good idea as we gain thread-safety and immutability which makes the code easier to reason about. I'm curious if there's an associated performance cost.

The Java Memory Model guarantees this final Field Semantics:

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

This means that for a class like this

class X {
    X(int a) {
        this.a = a;
    }
    final int a;

    static X instance;
}   

whenever Thread 1 creates an instance like this

X.instance = new X(43);
while (true) doSomethingEventuallyEvictingCache();

and Thread 2 sees it

 while (X.instance == null) {
      doSomethingEventuallyEvictingCache();
 }
 System.out.println(X.instance.a);

it must print 43. Without the final modifier, the JIT or the CPU could reorder the stores (first store X.instance and then set a=43) and Thread 2 could see the default-initialized value and print 0 instead.

When JIT sees final it obviously refrains from reordering. But it also has to force the CPU to obey the order. Is there an associated performance penalty?

Altonaltona answered 7/5, 2014 at 20:0 Comment(3)
All final fields are automatically volatile (visibility is guaranteed)Officinal
@Steve Visibility is guaranteed but the performance cost between volatile variables and finals is completely different. You don't need any memory barriers around reading finals.Stereoscopic
#138368Representationalism
K
8

Is there an associated performance penalty?

If you take a look at the source code of the JIT compiler, you will find the following comment regarding final member variables in the file src/share/vm/opto/parse1.cpp:

This method (which must be a constructor by the rules of Java) wrote a final. The effects of all initializations must be committed to memory before any code after the constructor publishes the reference to the newly constructor object. Rather than wait for the publication, we simply block the writes here. Rather than put a barrier on only those writes which are required to complete, we force all writes to complete.

The compiler emits additional instructions if there are final member variables. Most likely, these additional instructions cause a performance penalty. But it's unclear, if this impact is significant for any application.

Krystalkrystalle answered 7/5, 2014 at 20:7 Comment(3)
There is a very good paper by Mr Benchmarking himself on the freeze penalty for finals: shipilev.net/blog/2014/all-fields-are-finalSupersaturate
+1 Most likely, these additional instructions cause a performance penalty. On an X86 it issues a store-load fence which will incur a minor performance hit, but gains are probably better then that hit.Antiquary
@JohnVint - Is that from checking the asm? If so, it sounds too conservative / unnecessary. The linked source code from this answer does _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final());. In x86 asm, a release barrier is free, no instruction needed, just block compile-time reordering. mfence is actually very expensive, especially on recent Intel (Skylake) where it also blocks out-of-order exec. (Are loads and stores the only instructions that gets reordered?). (a dummy lock is cheaper; IIRC HotSpot might use that when it needs seq-cst)Bitstock

© 2022 - 2024 — McMap. All rights reserved.