지연 초기화(lazy initialization)는 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다.

  • 그 값이 쓰이지 않으면 초기화또한 일어나지 않는다.
  • 이 기법은 정적 필드와 인스턴스 필드 모두에 사용할 수 있다.
  • 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다.
  • 지연 초기화의 기본 메커니즘은 ' 필요할 때 까지 하지 말라 ' 이다.
  • 어떻게 보면 초기화 비용이 줄어서 좋아보이지만 접근성이 떨어져 양날의 검이 될 수 있다.
  • 지연 초기화가 필요한 경우
    • 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 큰 경우
    • 하지만 지연 초기화 사용 전 후의 성능 체크를 해봐야한다.

 

 

대부분의 상황에서 일반적인 초기화가 지연초기화보다 낫다.

//인스턴스 필드를 초기화하는 일반적인 방법
private final FieldType field = computeFieldValue();



//지연 초기화 - synchronized 접근 방식
private FieldType field;

private synchronized FieldType getField() {
    if (field == null) {
        field = computeFieldValue();
    }
    return field;
}


//성능 때문에 정적 필드를 지연초기화해야 하는 경우에 사용하는 지연 초기화 홀더 클래스 관용구
//정적 필드용 지연 초기화 홀더 클래스 관용구
private static class FieldHolder {
    static final FieldType field = computeFieldValue();
}

private static FieldType getField() { return FieldHolder.field; }

 

 

 

//성능 때문에 인스턴스 필드를 지연 초기화해야 한다면 이중검사 관용구를 사용하라

private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if (result != null) { // 첫 번째 검사 (락 사용 안함)
        return result;
    }

    synchronized(this) {
        if (field == null) { // 두 번째 검사 (락 사용)
            field = computeFieldValue();
        }
        return field;
    }

}


// 단일검사 관용구 - 초기화가 중복해서 일어날 수 있다.
private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if( result == null) {
        field = result = computFieldValue();
    }
    return result;
}

 

 

정리

  • 대부분의 필드는 지연시키지 말고 곧바로 초기화해야 한다.
  • 사용해야 한다면 올바른 지연 초기화 기법을 사용하자
  • 인스턴스 필드에는 이중검사 관용구를, 정적 필드에는 지연초기화 홀더 클래스 관용구를 사용하자

+ Recent posts