역사적으로 게으른 초기화는 리소스 사용 최적화를 위해 등장했습니다. 객체는 실제로 필요할 때만 생성됩니다. 원래는 무거운 객체, 연결, 캐시를 사용할 때나 복잡한 패턴(예: Singleton)을 구현할 때 중요했습니다.
문제는 모든 리소스를 즉시 생성하면 객체가 필요하지 않을 때도 불필요한 메모리, 시간 및 성능을 낭비할 수 있다는 것입니다. 게으른 초기화는 ‘지연된’ 생성 문제를 해결합니다.
해결책은 보통 체크를 통해 구현됩니다: 객체가 아직 생성되지 않았다면 생성하고, 그렇지 않으면 기존 객체를 반환합니다.
코드 예시:
public class LazyHolder { private Resource resource; public Resource getResource() { if (resource == null) { resource = new Resource(); } return resource; } }
멀티스레드 환경에서는 동기화가 필요합니다.
주요 특징:
Double-Checked Locking이 게으른 초기화를 위한 신뢰할 수 있는 구현인가요?
Java 5부터, 이전에는 메모리 모델이 이를 보장하지 않았기 때문입니다. 리소스 필드에 대해 volatile 키워드를 사용해야 합니다.
private volatile Resource resource; public Resource getResource() { if (resource == null) { synchronized(this) { if (resource == null) { resource = new Resource(); } } } return resource; }
가벼운 객체에 대해 게으른 초기화를 하는 것이 의미가 있나요?
보통은 그렇지 않습니다. '가벼운' 객체는 즉시 생성하여 코드의 가독성을 저해하지 않는 것이 좋습니다.
객체 직렬화 시 게으른 초기화가 작동하나요?
항상 그렇지는 않습니다. 직렬화 시 '이전' 상태로 복원되거나 readObject()에서 추가 논리가 필요할 수 있습니다.
고부하 서비스에서 특별한 객체 풀을 게으르게 파싱했지만 동기화되지 않았습니다. 두 스레드가 동시에 객체를 초기화하여 메모리 누수 및 예측할 수 없는 오류를 발생시켰습니다.
장점:
단점:
대규모 웹 애플리케이션에서 분석은 게으른 초기화된 프록시 객체를 통해 API 호출에 의해서만 연결됩니다.
장점:
단점: