Wednesday, June 1, 2011

How can final fields appear to change their values in old Java Memory Model

One of the best examples of how final fields' values can be seen to change involves one particular implementation of the String class.
A String class is immutable in java. So are the wrapper classes like Integer, Double. But they provide corresponding mutable classes like StringBuffer for String and BitSet for Integer. The reason why these classes are immutable because of security. Methods in IO take String as parameter and not StringBuffer. That is because of security reason.
A String can be implemented as an object with three fields ( normal string class have even more, but consider 3 for now) -- a character array, an offset into that array, and a length. So they are:
final char[] buffer = new char[1000];//say 1000;
final int offset;
final int length;

The rationale for implementing String this way, instead of having only the character array, is that it lets multiple String and StringBuffer objects share the same character array and avoid additional object allocation and copying.

Suppose now you call substring() method on this String. A new string is returned by this call.But String.substring() can be implemented by creating a new string which shares the same character array with the original String and merely differs in the length and offset fields. For a String, these fields are all final fields. See - How substring works in Java?

String s1="Kinshuk";
String s2=s1.substring(4);//returns string huk

Now this is 1 parameter substring method which takes beginIndex. So substring starts from 4th character(numbering starts from 0). So it is h.

So now what happens is since substring() returns new String, it will call the constructor. Now constructor will further call Object class constructor, which will call JVM to allocate memory, and further do default initialization and returns the address (or reference) back to new string. But like I said, this array remains the same just the offset and length change. Due to default initialization length and offset have value 0. Now it is possible for the Thread T2 say to see the value of substring s2 and empty because offset and length are 0 due to default initialization rather than 4 and 3 respectively. Another case may be like length is read fine by the thread but not offset. So it may read Kin sometimes and sometimes huk.

The original Java Memory Model allowed this behavior; several JVMs have exhibited this behavior. The new Java Memory Model makes this illegal.

So what is the solution for this?

Some say make constructor synchronized. But this was not a proper solution. Rather Java came up with new JMM and it solved some of the issues.
We'll see this here - How does final fields work under new JVM?

No comments:

Post a Comment