Thursday, January 25, 2007

Lazy Loading Singletons

I woke up this morning thinking I knew all there was to know about lazy loading singletons in Java. Boy, was I wrong. Java doesn't cease to surprise me, even after all these years.

First, why would you want to lazy load a singleton? In production, you typically want to eagerly load all your singletons so you catch errors early and take any performance hit up front, but in tests and during development, you only want to load what you absolutely need so as not to waste time.

Before Java 1.5, I lazy loaded singletons using plain old synchronization, simple but effective:

static Singleton instance;

public static synchronized Singleton getInstance() {
  if (instance == null)
    instance == new Singleton();
  return instance;
}
Changes to the memory model in 1.5 enabled the infamous Double-Checked Locking (DCL) idiom. To implement DCL, you check a volatile field in the common path and only synchronize when necessary:
static volatile Singleton instance;

public static Singleton getInstance() {
  if (instance == null) {
    synchronized (Singleton.class) {
      if (instance == null)
        instance == new Singleton();
    }
  }
  return instance;
}
But volatile isn't that much faster than synchronized, synchronized is pretty fast nowadays, and DCL requires more code, so even after 1.5 came out, I continued using plain old synchronization.

Imagine my surprise today when Jeremy Manson pointed me to the Initialization on Demand Holder (IODH) idiom which requires very little code and has zero synchronization overhead. Zero, as in even faster than volatile. IODH requires the same number of lines of code as plain old synchronization, and it's faster than DCL!

IODH utilizes lazy class initialization. The JVM won't execute a class's static initializer until you actually touch something in the class. This applies to static nested classes, too. In the following example, the JLS guarantees the JVM will not initialize instance until someone calls getInstance():

static class SingletonHolder {
  static Singleton instance = new Singleton();    
}

public static Singleton getInstance() {
  return SingletonHolder.instance;
}
Why didn't IODH register in my brain sooner? I'm on the JMM mailing list after all. I think I had a mental block due to the fact that exceptions thrown in class initializers used to be difficult to debug, i.e. before nested exceptions. If I recall correctly, you would get an ExceptionInInitializerError (which would tromp the root exception) followed by a series of ClassNotFoundExceptions. Lazy loading from an application thread didn't suffer this problem.

Today, exceptions in static initializers are easy enough to diagnose--they appear nested in the ExceptionInInitializerError, so use IODH from now qualm free.

Update: Credit where credit is due, Effective Java (copyright 2001) detailed this pattern under item 48. It goes on to point out that you still have to use synchronization or DCL in non-static contexts.

I also switched singleton handling in my framework from synchronization to DCL and saw another 10% performance boost (compared to before I started using cglib's fast reflection). I only used one thread in my micro-benchmark, so the boost to concurrency could be even greater given that I replaced a heavily contended lock with a relatively fine grained volatile field access.

24 Comments:

Anonymous Colin Sampaleanu said...

This may require the same number of lines of code as the sync approach, but you _are_ introducing a new class to be managed. I guess that's actually not that big a deal for this idiom, you're not going to be doing this all over the place. I do wonder about the implications of some other programming idioms. Heavy use of anonymous inner classes to work in a functional style could easily balloon the number of total classes in a big piece of software, all needing to be managed by the classloader...

7:05 AM  
Blogger Bob said...

I wouldn't worry about hand-written classes like this. By the time you start maxing out your permgen, you're almost guaranteed to max out other resources as well, and you will have to distribute your application anyway.

9:54 AM  
Anonymous Anonymous said...

how are those solutions better than simply

static Singleton instance = new Singleton();

because somehow you need to access the Singleton class without accessing the singleton instance? why is that?

11:42 AM  
Blogger Bob said...

I rarely write a standalone singleton class with nothing but a getInstance() method. Also, even if your singleton instance is the only static state in the class right now, I might still use a nested holder so the lazy loading behavior doesn't change when someone comes along later and adds something new to the class.

12:36 PM  
Anonymous Anonymous said...

The initialize-on-demand holder idiom is also discussed in Effective Java (Item 48: Synchronize access to shared mutable data)

3:21 PM  
Anonymous Anonymous said...

Here's a variation. Say, you have to enhance a purely static utility class FooUtil. One of the methods, analyzeIncomingFoo, might take longer to execute as requirements have evolved to include an email notification; so it is desirable to run the body on a separate thread. A solution might be to create an static inner class FooThreadPoolHolder.

3:36 PM  
Blogger Bob said...

I'll be damned. It is in Effective Java. It goes on to say, "the only shortcoming of the idiom is that it does not work for instance fields, only for static fields."

9:15 PM  
Anonymous Anonymous said...

I think the flux-capacitor is broke. Good thing you are a tool box so you can fix it.

In Hoc,

West, Paul Leoffleman and Cory Hoots- Yes we are drinking.

8:22 PM  
Blogger Alexander said...

It seems like a fascinating, superior way to do singletons- But- Given that there's no synchronization or volatile variables anywhere- I'm a little confused as to, with:

static Singleton instance = new Singleton();

How is the above statement atomic?

4:37 PM  
Blogger Bob said...

The JVM ensures the static initializer executes before anyone can access the class. There is synchronization (that of class loading), but we only pay for it up front (not every time you access the singleton).

5:41 PM  
Anonymous Anonymous said...

You can load the instance before getInstance is called using:

class.forName("Singleton$SingletonHolder");

This works even if SingletonHolder is private.

7:01 AM  
Blogger Bob said...

Yeah... you can force a static initializer to run a lot of ways, but why would you do that given that you already went out of your way to ensure it executes lazily? I also already linked to the section of the JLS which specifies these ways above.

7:53 AM  
Anonymous Anonymous said...

In initial post you write:

"In the following example, the JLS guarantees the JVM will not initialize instance until someone calls getInstance()"

Now you say:

"you can force a static initializer to run a lot of ways,"

These two statements can not both be true. You are basically saying 'I wrote one thing but I linked to a page that contracts that statement so it's not incorrect.'

Maybe you have a different idea of what the term 'guarantee' means?

"but why would you do that given that you already went out of your way to ensure it executes lazily?"

I wouldn't but something else could. A framework that uses reflection, perhaps. In any event, the point is that the JLs definitely does not guarantee that the singleton will not be initialized before getInstance is called if you use this idiom. The standard lazy loading idiom is not affected by forced class initialization via reflection.

People have been making this claim about it being 'guaranteed' for years. Since it's old news that's new again, I think it's a good time to point out this caveat. Not so much for this specific case but people might make use of this lazy-loading technique in other sceanrios where this wouldn't be strange. I don't see why you have a problem with being accurate in your statement. What's the downside?

8:23 AM  
Blogger Bob said...

Ha ha. OK. I just assume people know to use Class.forName(String, boolean, ClassLoader) instead of Class.forName(String) in reflective frameworks. Thanks for pointing this out.

For what it's worth, I typically want lazy loading during development but eager loading in production (so I catch errors early and take the performance hit up front). You could call Class.forName() on a given set of classes in production. This would be more difficult to automate with the synchronization approach.

8:42 AM  
Blogger Bob said...

I also said, "in this example," and I didn't use Class.forName() in my example, so I think I'm in the clear. ;)

8:45 AM  
Anonymous Anonymous said...

Just to be clear, I seriously doubt this will be a problem people will run into. I know it seems pedantic to keep going on about this but I've seen problems occur where people believe something can't happen but it can in rare circumstances (DCL is a good example of this.) These are really hard problems to resolve because people will not consider things they believe to be impossible.

The only scenario that comes to mind for me is if someone gets a bright idea to force all the classes to initialize up-front to avoid slow-downs during runtime. For example, I use something generated from SableCC that has a couple hundred classes. The first time I use it, it takes up to 20 seconds to load the classes (well, that's my working theory anyway.) For my app it doesn't matter but for something that involves users, you might want to load early. One could write a classpath parser that forces initialization on each class.

And while dev is a good example of a common reason for lazy-loading, there are rare circumstances that require it for correctness. Probably most if not all could be fixed with design changes but they still exist.

Again, I don't mean to be a pain in the ass, it just seems that people have been throwing the word 'guaranteed' around a little too lightly in regards to this.

BTW - this is the James from the TSS thread.

9:08 AM  
Blogger Bob said...

> BTW - this is the James from the TSS thread.

How'd I guess? ;)

Thanks, James.

9:13 AM  
Anonymous Sony Mathew said...

Relying on some obscure sequence of class loading to do lazy initialization? Its a neat trick but NOT Effective Java.

11:38 AM  
Blogger Bob said...

I disagree. Repeating the same boilerplate lazy loading logic and subjecting every request to unnecessary synchronization is not effetive.

1:28 PM  
Blogger haijiang said...

if write as this can be more briefly!
interface SingletonHolder {
Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}

10:49 PM  
Blogger Mircha Emanuel `ryuujin` D'Angelo said...

I think the best solution, lazy-loading and thread safe is this:
public class FooSingleton {

private FooSingleton() {}

private static class Container {
private final static FooSingleton fooSingleton = new FooSingleton();
}

public static FooSingleton getInstance() {
return Container.fooSingleton;
}
}

3:22 AM  
Blogger benroll said...

I think the volatile DCL way remains usefull when you need a singleton to remain single among different classloaders, and because of the classforname() ability to bypass Bob's singleton.

I love the use of the interface singularity made by haijiang It must be the only way to justify the ability for interfaces to hold Objects.
I am looking forward to find a way to make this kind of "interface-singleton" volatile.

4:11 AM  
Blogger benroll said...

What about
public enum EnumSingleton {
INSTANCE;
public void method(){
System.out.println("method called.");
}
}

?

8:48 AM  
OpenID javarevisited said...

Hi dude,Nice post , just to add
While writing Singleton class we need to consider many points e.g.
1) Lazy initialization
2) Early initialization
3) Serialization
4) Many ClassLoaders
5) Cloning

to tackle all above problem best way is to use JAVA 5 Enum functionality and write Singleton using Enum like below.
public enum Singleton {
INSTANCE;

public static void hi(){
System.out.println("Hi");
}
}

Thanks
Javin
Why String is immutable in Java

6:48 AM  

Post a Comment

Links to this post:

Create a Link

<< Home