<< 메모리 overcommit | Home | MQTT Packet 모니터링 >>

Singleton, Lazy loading 그리고 WeakSingleton

최근에 푸시 솔루션(MQTT 기반)을 만들면서 데몬 프로그래밍을 하다가 Singleton 패턴을 적용한 부분이 있어, 이부분에 대한 정리가 필요할 것 같아서 몇자 정리해 봅니다.
싱글톤 패턴의 변천사로 봐도 무방하리라 생각됩니다.

static inner class를 사용한 하나의 인스턴스만 생성하는 Singleton

이 경우 synchronized된 getInstance() 함수를 매번 호출하게 되는 문제가 있다. 실제로는 한번만 호출되어도 되는데...성능 이슈가 있다는 말이다.
public class Singleton {
  private static Singleton instance;

  public static synchronized Singleton getInstance() {
    if (instance == null)
      instance = new Singleton();
    return instance;
  }

}

그래서, 성능 저하를 방지하기 위해 Double-checked locking(DCL) 방법이 고안되었지만, Java에서는 DCL이 제대로 작동하지 않을 수 있다는 문제점이 존재한다.
그 예로 out-of-order 쓰기에 의해 인스턴스의 초기화전에 instance가 null이 아닐 수 있으며, 초기화가 끝나지 않은 인스턴스를 반환할 수도 있다.
이를 보완하기 위해 아래와 같이 JDK1.5 이상에서는 메모리 모델의 변경으로 volatile 한정자를 붙이는 것으로 Double-checked locking의 문제점을 피할수 있게 되었다. 하지만, volatile 비용은 높다.
static volatile Singleton instance;

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

Lazy Loading Singleton

인스턴스 생성을 지연할 경우 Initialization-on-demand holder(IODH) 방식을 사용할 수 있다. Nested static class가 사용하고, 그 클래스의 초기화가 해당 클래스에 처음 액세스 할 때 수행되게 하는 언어는 Initialization-on-demand holder라고 하는 idiom으로 변수의 초기화를 지연할 수 있다. 클래스의 초기화는 동기화 되므로, 멀티 스레드 프로그램에서도 다중으로 초기화 되지 않으며 재정렬에 의해 초기화가 불완전한 변수를 참조 버리는 일도 없어진다.
public class Singleton {
  private static class SingletonHolder {
    public static Singleton instance = new Singleton();
  }

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

WeakSingleton

Singleton 클래스를 보유하고 있는 클래스 로더가 삭제되지 않는 한, Singleton 개체가 생존하고 계속 존재하는 문제점이 있다.
아래는 자원을 회수할 수 있는 Singleton 클래스를 만드는 방법이다. WeakReference 클래스를 이용해, 외부 프로그램이 getInstance()가 반환하는 Singleton 객체를 참조하게 하고 Singleton 인스턴스가 강한 참조가 아니여서 GC에 의해 Singleton 인스턴스는 회수된다. 그러므로써 Java VM의 메모리 사용량이 줄어든다.
public class Singleton {
    private Singleton() {}
 
    public static Singleton getInstance() {
        synchronized(SingletonHolder.class){
            Singleton referent = SingletonHolder.instance.get();
            if(referent == null){
                referent = new Singleton();
                SingletonHolder.instance = new WeakReference<>(referent);
            }
            return referent;
        }
    }
 
    private static class SingletonHolder {
        public static Reference instance = 
	  new WeakReference<>(new Singleton());
    }
}

[참조 사이트]


Re: Singleton, Lazy loading 그리고 WeakSingleton

 깔끔하게 잘 정리되었네요.


Add a comment Send a TrackBack