Skip to content

[아이템 83] 지연 초기화는 신중히 사용하라 #83

@janeljs

Description

@janeljs

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

지연 초기화

  • 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
  • 값이 안 쓰이면 초기화도 X
  • 정적 필드와 인스턴스 필드 모두에 사용 가능
  • 최적화나 클래스&인스턴스 초기화 때 발생하는 순환문제 해결하는 효과

지연 초기화가 필요할 때까지는 하지 말라

  • 클래스 또는 인스턴스 생성 시 초기화 비용은 줄지만 필드에 지연초기화하는 필드에 접근하는 비용은 커짐
  • 대부분 일반적인 초기화가 낫다

지연 초기화가 필요할 때

  • 지연 초기화하려는 필드를 사용하는 인스턴스 비율은 낮은데 초기화 비용은 큰 경우
  • 여러 스레드가 지연 초기화 필드를 공유하면 버그 발생 가능 (동기화해야 함)

인스턴스 필드를 초기화하는 일반적인 방법

private final FieldType field = computeFieldValue();

인스턴스 필드의 지연 초기화 - synchronized 접근자 방식

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

초기화 순환성 깨기 위해 지연초기화를 사용하고 싶다면 synchronized를 사용하라는 것 같음

private FieldType field;

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

정적 필드용 지연 초기화 홀더 클래스 관용구

지연 초기화 홀더 클래스(lazy initialization holder class) 관용구: 클래스는 클래스가 처음 쓰일 때 비로소 초기화된다는 특성을 이용한 관용구

private static class Fieldholder {
    static final FieldType field = computeFieldValue();
}

private static FieldType getField() { return FieldHolder.field;}
  1. getField 호출
  2. FieldHolder.field가 처음 읽히면서, FieldHolder 클래스 초기화
  • getField() 메서드가 필드에 접근하면서 동기화를 사용하지 않아 성능 저하 우려 X
  • 대부분 VM은 클래스를 초기화할 때만 필드 접근을 동기화
  • 클래스 초기화가 끝난 후에는 VM이 동기화 코드를 제거하여, 그 다음부터는 아무런 검사나 동기화 없이 필드에 접근

인스턴스 필드 지연 초기화용 이중검사 관용구

  • 초기화된 필드에 접근할 때의 동기화 비용을 없애준다.
  • 첫 번째는 동기화 없이, 두 번째는 동기화하여 필드의 값을 검사
  • 두 번째 검사에서도 초기화 안 된 경우만 필드를 초기화
  • 필드 초기화 이후에는 동기화하지 않으므로 volatile로 선언 (java 변수를 cpu cache가 아닌 메인 메모리에 저장)
private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if (result != null) { // 첫 번째 검사 (락 사용 안 함)
        return result;
    
    synchronized(this) {
        if (field == null) // 두 번째 검사 (락 사용)
            field = computeFieldValue();
        return field;
    }
}
  • result: 필드가 이미 초기화된 상황에서 해당 필드를 한번만 읽도록 보장하는 역할

단일검사(single-check) 관용구

  • 반복해서 초기화해도 상관없는 인스턴스 필드를 지연초기화해야 할 때 사용
  • 두 번째 검사 생략
  • 필드는 volatile로 선언
private volatile FieldType field;

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

짜릿한 단일검사(Racy Single-Check) 관용구

핵심 정리

  • 대부분의 필드는 지연시키지 말고 바로 초기화하자.
  • 꼭 지연 초기화를 써야 한다면 인스턴스 필드에는 이중검사 관용구를, 정적 필드에는 홀더 클래스 관용구를 사용하자.
  • 반복해 초기화해도 괜찮은 인스턴스 필드에는 단일검사 관용구를 사용해도 된다.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions