본문 바로가기

책정리/EfectiveJava

지연 초기화는 신중히 사용하라

지연 초기화(Lazy initialization) 

 - 필드의 초기화 시점을 늦춰 처음 필요로 할 때 값을 초기화

 - 값이 쓰이지 않으면 초기화도 일어나지 않음

 

지연 초기화는 양날의검이다.

 - 클래스 혹은 인스턴스 생성 시 초기화 비용은 줄지만 지연 초기화하는 필드에 접근하는 비용이 커짐

지연 초기화 성능을 느려지게 하는 요소

   1. 초기화가 이뤄지는 필드에 비율

   2. 실제 초기화에 드는 비용

   3. 초기화된 필드를 얼마나 빈번히 호출 하느냐에 따라

지연 초기화가 필요할 때

  1 . 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮음

  2. 필드를 초기화하는 비용이 큼

 

정말 그런지 유일하게 알 수 있는 방법은 지연 초기화 적용 전후의 성능을 측정 해봐야 함

멀티 스레드 환경에서는 지연 초기화 하기가 까다롭다

 - 필드를 둘 이상 스레드가 공유 한다면 어떠한 형태로든 동기화 해야함

 

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

 

//동기화 된 접근자를 사용한 객체 필드 초기화 지연방법
  private FieldType fieldType;

  synchronized FieldType getField() {
    if (fieldType == null) {
      fieldType = new FieldType();
    }
    return fieldType;
  }

지연 초기화 순환성을 깨뜨릴 것 같으면 synchronized를 단 접근자를 사용하자.

성능 문제 때문에 정적 필드 초기화를 지연시키고 싶다면 지연 초기화 홀더 클래스(lazy initialization holder class)를 사용하자

 

 //정적 필드에 대한 초기화 지연 담당 클래스 
  private static class FieldHolder{
    static final FieldType field = new FieldType();
  }
  static FieldType getFieldType(){
    return FieldHolder.field;
  }

getField가 처음 호출되는 순간 FieldHoder.field가 처음 읽히면서 FieldHolder클새르 초기화를 촉발 시킨다.

동기화를 전혀 하지 않아 성능이 느려질 거리가 전혀 없다.

 

 

성능 문제 때문에 객체 필드 초기화를 지연시키고 싶다면 이중 검사(double-check)를 사용

private volatile FieldType field;

private FieldType getField(){
      FieldType result = field;
      
      if (result != null) {
      	return result;
      }
      
      synchronized(this) {
      	if (field == null) {
        	field = computeFieldValue();
        }
        return filed;
      }     
      
}

 

한 번은 동기화 없이 체크하고 두 번째는 동기화 하여 체크 한다.

필드가 초기화된 후로는 동기화를 하지 않으므로 ㅎ반드시 volatile로 선언 해야 함.

 

result 지역 변수가 필요한 이유

- 필드가 이미 초기화 된 상황에서는 한 번만 읽도록 보장하는 역할

-반드시 필요하지 않지만 성능을 높여준다.

 

 

 

대부분 필드는 지연시키지 말고 곧바로 초기화 하자

꼭 써야 한다면 올바른 지연 초기화 기법을 사용하자.