Java Singleton Problems

Using the SingletonPattern in a Java application seems to be a straight forward task. However, the reality is different. There are at least the following issues in Java when it comes to the singleton design patterns (detailed descriptions follow this list):


Garbage collection of the singleton instance

The "classic" example of a singleton implementation in Java looks like:

 public class LogManager
 {
     private java.io.PrintStream m_out;

private LogManager( PrintStream out ) { m_out = out; }

public void log( String msg ) { System.out.println( msg ); }

static private LogManager sm_instance; static public LogManager getInstance() { if ( sm_instance == null ) sm_instance = new LogManager( System.out ); return sm_instance; } }

Usage:

 LogManager.getInstance().log( "some message" );

But does this work? Not in Java 1.1 up to Java 1.1.5 (excluding). And it is less of a problem in Java 2.

The Singleton implementation suggested by the GangOfFour has an interesting failure mode in Java: If the sole reference to the singleton is from the class, then the java 1.1 collector will collect the instance and unload the class. The next retrieval of the singleton will reload the class and manufacture a newly initialized singleton instance.

This is especially a problem is the singleton holds some state (e.g. a counter), or if the istantiation of the object has some side effects.

The solution is to register singleton instances in some system wide table so that it is protected from garbage collection. Consider using latent threads: Create a thread for each singleton at the time that the singleton instance is created. Do not schedule the thread to run, this is not the purpose of the thread. The mere existence of the thread will protect the singleton. Also consider using a registry class which maintains a collection of the singleton instances. Create one latent thread for the registry instance. Both the registry and any singleton instances it has references to will be protected from garbage collection and it only uses one thread.

Alternatively, store the instance in a system property when it's first instantiated, as a pre-built registry.

Note that with more recent versions of Java (post-JDK1.1.5 or so) this whole issue is no longer a big problem. Classes that are loaded via the default class loader can't be garbage-collected until their ClassLoader has been garbage collected, which means that

  1. You have explicit control, if you want it; and
  2. In typical applications and applets (even when classes are loaded using RMI) the right thing happens naturally.

The behavior that allowed classes to be collected while the ClassLoader was still reachable was acknowledged as a bug in the spec.

It's a minor problem in JDK1.2, still. The spec states that classes loaded by the default class loader will not be GC'ed; however, classes that are loaded by a custom class loader are eligble for GC. But, the classes loaded by a custom class loader can't be collected until the class loader itself has been collected. For that to happen, essentially all classes that ever referenced the singleton class have to also be unloaded first. You have to work to achieve such a situation.


Locking/Synchronisation

When using a SingletonPattern in a multithreaded environment, it has become common to also apply the DoubleCheckLock? pattern as an optimization for accessing the singleton instance:

public class BrokenSingleton {
  private BrokenSingleton() { }

public static BrokenSingleton getInstance() { if (_theInstance == null) { synchronized (BrokenSingleton.class) { if (_theInstance == null) { _theInstance = new BrokenSingleton(); } } } return _theInstance; } }

It turns out that this will not work reliably. This issue turns out to have become a major point of discussion and resulted in The "Double-Checked Locking is Broken" Declaration: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

See also: DoubleCheckedLockingIsBroken


Multiple class version

Each class loader in Java has its own name space. One can load the very same class through two or more different class loaders. If this class is a singleton, then each of the class loaders used to load the class holds an own instance of the singleton. This might not be the intention with a singleton.

The issue is used to circumvent AnotherJavaSingletonProblem


See also:


Contributors are (in no particular order): MartinPool, SteveFreeman, GadiGuy?, GlennVanderburg, MartinSchwartz, StevenNewton, ThomasWeidenfeller.


CategoryJava



EditText of this page (last edited October 11, 2002) or FindPage with title or text search