Monday, December 27, 2010

Re Java final keyword, immutability and reflection

Few days ago, in a conversation ("Sun Certified Java Programmer " group at http://www.linkedin.com/) regarding Immutability pattern and Java Reflection, somebody came with the following code example (slightly adjusted by me in order to highlight the class attributes initialization order), which I found quite interesting and decided to post it.

So, execute the following code:
import java.lang.reflect.Field; 

class Test {
 public Test() {
  doIt();
 }
 public void doIt() {}
}

public class TestReflection extends Test { 
 private final String name = "Immutable";
 private String n = "n";
 public TestReflection() { } 

 public void doIt() {
  System.out.println("1 = " + name); 
  System.out.println(n); 
  System.out.println("-----");
 }

 public static void main(String[] args) throws Exception { 
  TestReflection abc = new TestReflection();
  Class c1 = Class.forName("TestReflection");
  Field field2 = c1.getDeclaredField("name"); 
  field2.setAccessible(true); 

  System.out.println("2 = " + abc.name); 
  field2.set(abc, "Mutable"); 
  System.out.println("3 = " + abc.name); 
 } 
}

Output: 
1 = Immutable
null
-----
2 = Immutable
3 = Immutable

And the following version of the code:
import java.lang.reflect.Field;

class Test {
 public Test() {
  doIt();
 }
 public void doIt() {}
}
public class TestReflection extends Test { 
 private final String name; 
 private String n = "n";

 public TestReflection() { 
  name = "Immutable"; 
 } 

 public void doIt() {
  System.out.println("1 = " + name); 
  System.out.println(n); 
  System.out.println("-----");
 }

 public static void main(String[] args) throws Exception { 
  TestReflection abc = new TestReflection();
  Class c1 = Class.forName("TestReflection"); 
  Field field2 = c1.getDeclaredField("name"); 

  field2.setAccessible(true); 
  System.out.println("2 = " + abc.name); 
  field2.set(abc, "Mutable"); 
  System.out.println("3 = " + abc.name); 
 } 
}

Output: 
1 = null
null
-----
2 = Immutable
3 = Mutable

The answer to this behaviour can be found in JLS (http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html), section 17.5.3,

"Even then, there are a number of complications. If a final field is initialized to a compile-time constant in the field declaration, changes to the final field may not be observed, since uses of that final field are replaced at compile time with the compile-time constant. "

Wednesday, December 22, 2010

Java 32bits vs. 64bits

I was playing recently with a recursive method for building all the possible routes via a set of nodes (with some predefined rules to link each pair of nodes) in Java. Rather than persisting the calculated routes into a file, I keep them in a HashSet.

Having about 138 nodes, this recursive method generates more than 5mln routes. This is quite an intensive calculation that consumes (and this is the point)
- about 1GB RAM with 32 bits JVM and
- about 2GB with 64 bits JVM.

Execution time, for building the routes, is like 17 seconds with 32 bits JVM and 21 seconds with 64 bits JVM.

Obvious question is "What the heck?". Apparently, here are the problems:
1. 64 bits systems are supposed to use (correct) 64 bits pointers (please allow me to use this C/C++ term) for accessing the process address space, thus allowing the process to use/access more (virtual) memory (i.e. 4GB with 32 bits vs. 16TB with 64 bits). But keeping an allocation table for 64 bits pointers (comparing to 32 bits) requires much more memory. So, 1GB vs. 2GB RAM isn't really a JVM problem.

Conclusion: if at some point you decide to use 64 bits JVM, don't forget to adjust the heap volume accordingly, in order to avoid unpleasant surprises.

2. Problem is also related to the way memory chips (DDR SDRAM) are made. Without diving into details (more details here http://lwn.net/Articles/250967/), accessing physical memory is a very slow operation and CPU tends to store frequently accessed data in its cache memory (L1, L2 and/or L3, which are far better memory chips, more expensive though). Modern compilers also tend to help developers from this point of view, if they follow some very well known rules (e.g. use arrays, stack is "cache-able", don't use "volatile" unless you really know what it is, etc.). But larger pointers require different memory alignment, thus 64 bits pointers aren't so cache efficient in the way 32 bits pointers are (e.g. when thinking about arrays). As a result, accessing physical RAM (or CPU cache miss) is the result of the 4 seconds difference.

To conclude: take care when deciding about using 32 bits or 64 bits Java.

And few online resources:
http://benjchristensen.com/2007/02/16/32-bit-versus-64-bit-jdk-memory-usage/
http://portal.acm.org/citation.cfm?id=1107407