Dec 16 2011

Java library performance optimzation using local references to final members: should you use it?

Category: javaUlrich Palha @ 9:37 am

Mysterious java idiom

While I was browsing through the java.util.concurrent source code, I noticed widespread use of the following idiom

    private final ReentrantLock mainLock = new ReentrantLock();
        ...
        //local reference to final member variable
        final ReentrantLock mainLock = this.mainLock; 
        mainLock.lock(); 
        try {
            ...     
        } finally {
            mainLock.unlock();
    }

Given that member mainLock is final, I could not understand the benefit of declaring a local reference.

Examine the byte code

Curious, I created a simple class with and without this idiom, then looked at the generated bytecode.

   0:	aload_0		// load the this reference
   1:	getfield	#4; //Field mainLock:Ljava/util/concurrent/locks/ReentrantLock;
   4:	astore_1	//store reference to local variable

Both used the getfield opcode to initially fetch the reference to the mainLock member variable from the runtime constant pool. The version with a local reference obviously stored it in the local variable table and retrieved all subsequent references from there.

Since the member variable was final, I expected that the compiler would automatically optimize the version without the local reference to do the same. However, it did not: all subsequent references were also fetched from the runtime constant pool using the getfield opcode.

   16:	getfield	#4; //Field mainLock:Ljava/util/concurrent/locks/ReentrantLock;
   19:	invokevirtual	#9; //Method java/util/concurrent/locks/ReentrantLock.unlock:()V

Performance optimization?

After further research, I learned that Doug Lea, the author of most of the concurrent code, made this change in 2003 to cache finals across volatiles. He confirmed that this was a performance optimization because many if not all JVMs sometimes have trouble understanding that final fields do not need to be reloaded across locks/volatiles.

Should you optimize your java code this way?

If you listen to Michael A. Jackson, you should not even think about doing this unless you are an expert.

The First Rule of Program Optimization: Don’t do it.
The Second Rule of Program Optimization (for experts only!): Don’t do it yet. – Michael A. Jackson

Brian Goetz, author of Java Concurrency in Action, suggests that you don’t worry about it, as it only applies to the Client JIT compiler (i.e. not java -server).

I would listen to the experts and only think about using this technique if you have profiled your code and determined that this optimization would yield performance benefits that your JIT compiler does not automatically apply.

For now, your mind, like mine, should rest easy the next time you read some Java library concurrency or String code that uses this technique.

References/Further Reading

Java Memory Model Semantics
Java bytecode: Understanding bytecode makes you a better programmer

Tags: , , ,