Item 83 - 지연 초기화는 신중히 사용하라
지연 초기화는 신중히 사용하라
Intro
지연 초기화(lazy initialization)
필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
용도 및 효과
지연 초기화는 최적화 용도로 쓰인다.
클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다.
적용 대상
정적 필드
인스턴스 필드
지연 초기화
클래스
혹은인스턴스 생성
시의 초기화 비용은 줄지만, 지연 초기화하는 필드에 접근하는 비용은 커진다.지연 초기화하려는 필드
들 중 결국 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에 따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라지연 초기화
가 실제로는 성능을 느려지게 할 수도 있다.
지연 초기화가 필요한 경우
해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 크다면
지연 초기화
가 제 역할을 할 것이다.하지만 위 효과를 확인하기 위해서는
지연 초기화 작용 전후의 성능을 측정
해보는 것이다.
멀티 스레드 환경의 지연 초기화
멀티스레드 환경에서는 지연 초기화를 하기 힘들다.
지연 초기화하는 필드를 둘 이상의 스레드가 공유한다면 반드시 동기화를 해야 한다. 아이템 78
Thread Safe 한 지연 초기화
대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다.
관용구 1: 인스턴스 필드를 선언할 때의 일반적인 초기화
관용구 2: 인스턴스 필드의 지연 초기화 (synchronized 접근자를 통한 방식)
지연 초기화
가 초기화 순환성(initialization circularity) 을 깨뜨릴 것 같으면 synchronized를 단 접근자를 사용한다.
두 관용구(
보통의 초기화
와synchronized 접근자를 사용한 지연 초기화
)는 정적 필드에도 똑같이 적용된다.이때
필드
와접근자 메서드 선언
에 static 한정자를 추가해야 한다.
관용구 3: 정적 필드용 지연 초기화 홀더 클래스
성능면에서
정적 필드의 지연 초기화
가 필요한 경우 지연 초기화 홀더 클래스 관용구(lazy initialization holder class) 를 사용한다.클래스는
클래스가 처음 쓰일 때 비로소 초기화된다는 특성
을 이용한 관용구
getField가 처음 호출되는 순간 FieldHolder.field가 처음 읽히면서, 비로소 FieldHolder 클래스 초기화를 촉발한다.
getField 메서드가 필드에 접근하면서 동기화를 전혀 하지 않으니 성능이 느려질 거리가 전혀 없다.
성능이 느려질 이유가 없다?
일반적인 VM은 오직 클래스를 초기화할 때만 필드 접근을 동기화할 것이다.
클래스 초기화가 끝난 후에는 VM이 동기화 코드를 제거하여, 그 다음부터는 아무런 검사나 동기화 없이 필드에 접근하게 된다.
관용구 4: 성능적인 측면에서의 인스턴스 필드 지연 초기화를 위한 이중검사(double-check)
이 방법은 초기화된 필드에 접근할 때의 동기화 비용을 없애준다.아이템 79
동작 방식
필드의 값을 두 번 검사하는 방식
한 번은 동기화 없이 검사
두 번째는 동기화하여 검사
두 번째 검사에서도
필드가 초기화되지 않았을 때
만 필드를 초기화 한다.
주의 사항
필드가 초기화된 후로는 동기화하지 않으므로 해당 필드는 반드시 volatile 로 선언해야 한다. 아이템 78
코드 분석
result라는 지역변수의 용도는 필드가 이미 초기화된 상황(일반적인 상황에서)에서는 그 필드를 딱 한번만 읽도록 보장하는 역할을 한다.
반드시 필요하지는 않지만 성능을 높여주고, 저수준 동시성 프로그래밍에 표준적으로 적용되는 더 우아한 방법이다.
정적 필드에 대한 이중검사 관용구 적용 가능성
정적 필드를
지연 초기화
하기 위해서는 이중검사보다 지연 초기화 홀더 클래스 방식이 더 낫다.
이중 검사의 특이 유형 2가지
상황
반복해서 초기화해도 상관없는
인스턴스 필드를 지연초기화해야 하는 경우,이중검사
에서 두 번째 검사를 생략할 수 있다.
이중 검사의 특이 유형 1: 단일 검사(single-check)
필드는 volatile 로 선언
초기화가 중복해서 일어날 수 있다.
이중 검사의 특이 유형 2: 짜릿한 단일검사(racy single-check)
사용 조건
모든 스레드가 필드의 값을 다시 계산해도 되고, 필드의 타입이 long과 double을 제외한 다른 기본 타입이라면, 단일검사의 필드 선언에서 volatile 한정자를 없애도 된다.
효과
어떤 환경에서는 필드 접근 속도를 높여주지만, 초기화가 스레드당 최대 한 번 더 이루어질 수 있다.
아주 이례적인 기법으로, 보통은 쓰이지 않는다.
정리
여기서 다룬 모든 초기화 기법은 기본 타입 필드와 객체 참조 필드에 모두 적용할 수 있다.
이중검사와 단일검사 관용구를 수치 기본 타입 필드에 적용한다면 필드의 값을 null 대신(숫자 기본 타입 변수의 기본값인)0과 비교하면 된다.
대부분의 필드는 지연시키지말고 곧바로 초기화해야 한다.
성능 때문에 혹은 위험한 초기화 순환을 막기 위해 꼭 지연 초기화를 써야 하는 경우 올바른 지연 초기화 기법을 사용한다.
인스턴스 필드에는 이중검사 관용구, 정적 필드에는 지연 초기화 홀더 클래스 관용구를 사용한다.
반복해 초기화해도 괜찮은 인스턴스 필드에는 단일검사 관용구도 고려대상이다.
Last updated