TIL
  • Contents
  • Book
    • 도메인 주도 설계
      • 1. 동작하는 도메인 모델 만들기
    • 오브젝트
      • 데이터 중심 설계
      • 책임 중심 설계
      • 책임 할당을 위한 GRASP 패턴
      • 메시지와 인터페이스
      • 객체 분해
    • Effective Java
      • Item 7 - 다 쓴 객체 참조를 해제하라
      • Item 7 발표 내용
      • Item 13 - clone 재정의는 주의해서 진행하라
      • Item 13 발표 내용
      • Item 16 - public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
      • Item 16 발표 내용
      • Item 26 - 로 타입은 사용하지 말라
      • Item 28 - 배열보다는 리스트를 사용하라
      • Item 28 발표 내용
      • Item 29 - 이왕이면 제네릭 타입으로 만들라
      • Item 30 - 이왕이면 제네릭 메서드로 만들라
      • Item 31 - 한정적 와일드 카드를 사용해 API 유연성을 높이라
      • Item 35 - ordinal 메서드 대신 인스턴스 필드를 사용하라
      • Item 37 - ordinal 인덱싱 대신 EnumMap을 사용하라
      • Item 37 발표 내용
      • Item 43 - 람다보다는 메서드 참조를 사용하라
      • Item 43 발표 정리
      • Item 56 - 공개된 API 요소에는 항상 문서화 주석을 작성하라
      • Item 56 발표 정리
      • Item 62 - 다른 타입이 적절하다면 문자열 사용을 피하라
      • Item 62 발표 정리
      • Item 73 - 추상화 수준에 맞는 예외를 던지라
      • Item 83 - 지연 초기화는 신중히 사용하라
      • Item 83 발표 내용
      • Item 89 - 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
      • Item 89 발표 내용
    • 개발자를 위한 SQL 튜닝
      • SQL 쿼리 실습을 위한 DB 서버 구축
      • 인덱스 튜닝
      • 인덱스 스캔 튜닝
      • 인덱스 스캔 튜닝 실습
      • 인덱스 패스트 풀 스캔
      • 테이블 풀 스캔 튜닝
      • 조인 튜닝
      • 중첩 루프 조인 튜닝
      • 중첩 루프 조인 튜닝 실습
      • 해시 조인 튜닝
      • 해시 조인 튜닝 실습
      • 세미 조인 튜닝
      • 세미 조인 튜닝 실습
      • 아우터 조인
      • 함수 튜닝
      • 부분 범위 처리 튜닝
      • 파티셔닝 튜닝
      • 파티션 인덱스 튜닝
      • 병렬 처리 튜닝
  • Java
    • Design Pattern
      • Intro
      • Types of Design Patterns
      • Creational
        • Builder Pattern
        • Singleton Pattern
        • Prototype Pattern
        • Factory Pattern
        • Abstract Factory Pattern
      • Structural
        • Adapter Pattern
        • Bridge Pattern
        • Composite Pattern
        • Decorator Pattern
        • Facade Pattern
        • Flyweight Pattern
        • Proxy Pattern
      • Behavioural
        • Chain of Responsibility Pattern
        • Command Pattern
        • Interpreter Pattern
        • Iterator Pattern
        • Mediator Pattern
        • Memento Pattern
        • Observer Pattern
        • State Pattern
        • Strategy Pattern
        • Template Method Pattern
        • Visitor Pattern
    • Java
      • Cracking the Coding Interview
      • TDD, Clean Code with Java 11기
        • 자동차 레이싱
        • 로또
        • 사다리 타기
        • 볼링 게임 점수판
    • 궁금증
      • 자바 8 버전의 인터페이스와 추상클래스
      • 자바의 제네릭은 어떻게 이전 버전과 호환되는 걸까?
      • 스프링 MVC 기본 구조
      • 마샬링과 직렬화
      • 인터뷰 질문 모음
      • Code Coverage
  • Database
    • Database
      • SQL 레벨업
      • DB 스터디
        • DBMS
          • MySQL
        • INDEX
        • Join(Nested Loop, Hash)
        • Join(Semi, Outer)
        • Partial Range Processing
        • Function
        • Partitioning
        • Parallel Processing
  • Network
  • Architecture
    • Issue
      • Git Push Error
      • SonarLint Warning - assertThatExceptionOfType()
  • Infra
  • Spring
    • Spring JPA
      • 1. 데이터 모델링 및 연관관계 설정
      • 2. 최적화 내용
      • 3. Spring-Data-Jpa
      • 4. Query DSL
    • Spring Security
      • Intro
    • Spring Batch
      • 배치용 디비 설치
      • 배치 데이터 분석하기
      • 배치 프로세스 구상하기 및 성능 차이 확인하기
  • Issue
  • Tistory
    • Tistory Blog
  • Design High Performing Architectures
  • Design Resilient Architectures
  • Design Secure Applications And Architectures
  • Design Cost-Optimized Architectures
Powered by GitBook
On this page
  • Intro
  • 지연 초기화
  • 지연 초기화가 필요한 경우
  • 멀티 스레드 환경의 지연 초기화
  • Thread Safe 한 지연 초기화
  • 이중 검사의 특이 유형 2가지
  • 정리

Was this helpful?

  1. Book
  2. Effective Java

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

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

Intro

지연 초기화(lazy initialization)

  • 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법

  • 용도 및 효과

    • 지연 초기화는 최적화 용도로 쓰인다.

    • 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다.

  • 적용 대상

    • 정적 필드

    • 인스턴스 필드

지연 초기화

  • 클래스 혹은 인스턴스 생성 시의 초기화 비용은 줄지만, 지연 초기화하는 필드에 접근하는 비용은 커진다.

  • 지연 초기화하려는 필드들 중 결국 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에 따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라 지연 초기화가 실제로는 성능을 느려지게 할 수도 있다.

지연 초기화가 필요한 경우

  • 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 크다면 지연 초기화가 제 역할을 할 것이다.

  • 하지만 위 효과를 확인하기 위해서는 지연 초기화 작용 전후의 성능을 측정해보는 것이다.

멀티 스레드 환경의 지연 초기화

  • 멀티스레드 환경에서는 지연 초기화를 하기 힘들다.

Thread Safe 한 지연 초기화

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

관용구 1: 인스턴스 필드를 선언할 때의 일반적인 초기화

class Example {
    // final 한정자를 통한 인스턴스 필드 생성
    private final FieldType field = computeFieldValue();
}

관용구 2: 인스턴스 필드의 지연 초기화 (synchronized 접근자를 통한 방식)

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

class Example {
    private final FieldType field;

    private synchronized FieldType getField() {
        if (field == null) {
            field = computeFieldValue();
        }
        return field;
    }
}
  • 두 관용구(보통의 초기화와 synchronized 접근자를 사용한 지연 초기화)는 정적 필드에도 똑같이 적용된다.

  • 이때 필드와 접근자 메서드 선언에 static 한정자를 추가해야 한다.

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

  • 성능면에서 정적 필드의 지연 초기화가 필요한 경우 지연 초기화 홀더 클래스 관용구(lazy initialization holder class) 를 사용한다.

  • 클래스는 클래스가 처음 쓰일 때 비로소 초기화된다는 특성을 이용한 관용구

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

    private static FieldType getField() {
        return FieldHolder.field;
    }
}
  • getField가 처음 호출되는 순간 FieldHolder.field가 처음 읽히면서, 비로소 FieldHolder 클래스 초기화를 촉발한다.

  • getField 메서드가 필드에 접근하면서 동기화를 전혀 하지 않으니 성능이 느려질 거리가 전혀 없다.

성능이 느려질 이유가 없다?

  • 일반적인 VM은 오직 클래스를 초기화할 때만 필드 접근을 동기화할 것이다.

  • 클래스 초기화가 끝난 후에는 VM이 동기화 코드를 제거하여, 그 다음부터는 아무런 검사나 동기화 없이 필드에 접근하게 된다.

관용구 4: 성능적인 측면에서의 인스턴스 필드 지연 초기화를 위한 이중검사(double-check)

  • 동작 방식

    • 필드의 값을 두 번 검사하는 방식

    • 한 번은 동기화 없이 검사

    • 두 번째는 동기화하여 검사

    • 두 번째 검사에서도 필드가 초기화되지 않았을 때만 필드를 초기화 한다.

  • 주의 사항

class Example {
    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라는 지역변수의 용도는 필드가 이미 초기화된 상황(일반적인 상황에서)에서는 그 필드를 딱 한번만 읽도록 보장하는 역할을 한다.

    • 반드시 필요하지는 않지만 성능을 높여주고, 저수준 동시성 프로그래밍에 표준적으로 적용되는 더 우아한 방법이다.

  • 정적 필드에 대한 이중검사 관용구 적용 가능성

    • 정적 필드를 지연 초기화하기 위해서는 이중검사보다 지연 초기화 홀더 클래스 방식이 더 낫다.

이중 검사의 특이 유형 2가지

  • 상황

    • 반복해서 초기화해도 상관없는 인스턴스 필드를 지연초기화해야 하는 경우, 이중검사에서 두 번째 검사를 생략할 수 있다.

이중 검사의 특이 유형 1: 단일 검사(single-check)

  • 필드는 volatile 로 선언

  • 초기화가 중복해서 일어날 수 있다.

class Example {
    private volatile FieldType field;

    private FieldType getField() {
        FieldType result = field;

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

이중 검사의 특이 유형 2: 짜릿한 단일검사(racy single-check)

  • 사용 조건

    • 모든 스레드가 필드의 값을 다시 계산해도 되고, 필드의 타입이 long과 double을 제외한 다른 기본 타입이라면, 단일검사의 필드 선언에서 volatile 한정자를 없애도 된다.

  • 효과

    • 어떤 환경에서는 필드 접근 속도를 높여주지만, 초기화가 스레드당 최대 한 번 더 이루어질 수 있다.

  • 아주 이례적인 기법으로, 보통은 쓰이지 않는다.

정리

  • 여기서 다룬 모든 초기화 기법은 기본 타입 필드와 객체 참조 필드에 모두 적용할 수 있다.

  • 이중검사와 단일검사 관용구를 수치 기본 타입 필드에 적용한다면 필드의 값을 null 대신(숫자 기본 타입 변수의 기본값인)0과 비교하면 된다.

  • 대부분의 필드는 지연시키지말고 곧바로 초기화해야 한다.

  • 성능 때문에 혹은 위험한 초기화 순환을 막기 위해 꼭 지연 초기화를 써야 하는 경우 올바른 지연 초기화 기법을 사용한다.

  • 인스턴스 필드에는 이중검사 관용구, 정적 필드에는 지연 초기화 홀더 클래스 관용구를 사용한다.

  • 반복해 초기화해도 괜찮은 인스턴스 필드에는 단일검사 관용구도 고려대상이다.

PreviousItem 73 - 추상화 수준에 맞는 예외를 던지라NextItem 83 발표 내용

Last updated 3 years ago

Was this helpful?

지연 초기화하는 필드를 둘 이상의 스레드가 공유한다면 반드시 동기화를 해야 한다.

이 방법은 초기화된 필드에 접근할 때의 동기화 비용을 없애준다.

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

아이템 78
아이템 79
아이템 78