Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do final fields prevent other threads from seeing partially constructed objects?

I was looking into creating an immutable datatype that has final fields (including an array that is constructed and filled prior to being assigned to the final member field), and noticed that it seems that the JVM is specified to guarantee that any other thread that gets a reference to this object will see the initialized fields and array values (assuming no pointers to this are published within the constructor, see What is an "incompletely constructed object"? and How do JVM's implicit memory barriers behave when chaining constructors?).

I am curious how this is achieved without synchronizing every access to this object, or otherwise paying some significant performance penalty. According to my understanding, the JVM can achieve this by doing the following:

  1. Issue a write-fence at the end of the constructor
  2. Publish the reference to the new object only after the write-fence
  3. Issue a read-fence any time you refer to a final field of an object

I can't think of a simpler or cheaper way of eliminating the risk of other threads seeing uninitialized final fields (or recursive references through final fields).

This seems like it could impose a severe performance penalty due to all of the read-fences in the other threads reading the object, but eliminating the read-fences introduces the possibility that the object reference is seen in another processor before it issues a read-fence or otherwise sees the updates to the memory locations corresponding to the newly initialized final fields.

Does anyone know how this works? And whether this introduces a significant performance penalty?

like image 901
jonderry Avatar asked Aug 08 '13 01:08

jonderry


1 Answers

See the "Memory Barriers" section in this writeup.

A StoreStore barrier is required after final fields are set and before the object reference is assigned to another variable. This is the key piece of info you're asking about.

According to the "Reordering" section there, a store of a final field can not be reordered with respect to a store of a reference to the object containing the final field.

Additionally, it states that in v.afield = 1; x.finalField = v; ... ; sharedRef = x;, neither of the first two can be reordered with respect to the third; which ensures that stores to the fields of an object that is stored as a final field are themselves guaranteed to be visible to other threads before a reference to the object containing the final field is stored.

Together, this means that all stores to final fields must be visible to all threads before a reference to the object containing the field is stored.

like image 127
Jason C Avatar answered Sep 18 '22 17:09

Jason C