시작하기 전 ...
싱글턴을 생성하는 방법 중 Eager Initialization의 단점은 인스턴스를 미리 생성하기 때문에, 사용하지 않을 경우 메모리 점유로 인한 자원낭비가 된다였다. 여기서 나의 의문의 시작되는데,,,, 더보기 ..
https://dev-rosiepoise.tistory.com/75
[Singleton Pattern] 싱글톤 패턴 생성하기
Goal Singleton이 무엇인지 이해한다 Singleton을 생성하는 방법에 대해 알아보고 이해한다 Singleton Pattern 싱글턴 패턴이란 인스턴스를 하나만 만들어 사용하기 위한 패턴이다. 프로그램 시작부터 종료
dev-rosiepoise.tistory.com
멘토님은 클래스 로딩 동작과정을 이해할 필요가 있다고 말씀해주셨고,
좀 더 정확히 싶은 마음이 생겼다!
Goal
- 클래스 로더에 대해 알아보고 이해한다
- 클래스 로딩과 클래스 초기화에 대해 알아보고 이해한다!
클래스 로더란 ?
자바 바이트 코드를 읽어서 JVM의 실행 엔진이 사용할 수 있도록 메모리의 메소드 영역에 적재하는 역할을 한다
클래스 로딩 (Class Loading)이란 ?
클래스 로딩은 클래스 로더에 의해 수행되며 해당 클래스의 바이트 코드를 메모리로 로드하는 과정을 의미한다.
클래스 로딩의 단계
- 로드(Loading): 클래스 로더가 .class 파일을 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고, 메소드 영역에 저장
- 연결(Linking): 클래스 로더는 로드한 클래스의 바이트 코드를 검증하고, 필요한 리소스를 준비
- 초기화(Initialization): 클래스의 정적 변수를 실제 값으로 초기화하고, 정적 초기화 블록(static initialization block)이 있다면 실행. 이때 클래스의 초기화는 필요한 경우에만 수행
클래스 로딩 VS. 클래스 초기화
클래스나 클래스 내의 static 멤버들을 소스를 실행하자마자 한번에 메모리에 모두 올라가는줄 착각하는데, 곰곰히 생각해보면 언제 어디서 사용될지 모르는 static 멤버들을 처음에 전부 메모리에 올린다는건 비효율적이기 때문에, 클래스 내의 멤버를 호출하게 되면 그때서야 클래스가 동적으로 메모리에 로드된다.
즉, JVM은 실행될때 모든 클래스를 메모리에 올려놓지 않고, 그때 마다 필요한 클래스를 메모리에 올려 효율적으로 관리하는 것이다.
******* 클래스가 제일 처음 사용되는 3가지 경우 = 클래스 로더에 의해 해당 클래스가 로딩되는 시점
- 클래스의 정적 속성을 사용시
- 단, final로 선언되면 안됨
- 클래스의 정적 메서드를 사용시
- 클래스의 인스턴스를 최초로 생성 시
- 단, 내부 클래스는 직접 인스턴스를 생성하지 않으니 로드되지 않는다.
https://dev-rosiepoise.tistory.com/74
[객체지향의 원리와 이해 4] 자바가 확장한 객체 지향
abstract 키워드 - 추상 메서드와 추상 클래스 추상 메서드란 선언부는 있지만 구현부가 없는 메서드를 말한다. 추상 메서드를 하나라도 갖고 있는 클래스는 반드시 추상 클래스여야 한다. public cla
dev-rosiepoise.tistory.com
JVM 옵션 -verbose:class
클래스 로딩
클래스 로더가 .class 파일을 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고, 메소드 영역에 저장
1. 아무것도 호출하지 않은 경우
public class Single {
// 1. 생성자
public Single(){
System.out.println("생성자");
}
// 2. 정적변수 final x
public static int a;
// 3. 정적변수 final
public static final int b=0;
// 4. 정적 메서드
public static void getInstance(){
System.out.println("정적 메서드");
}
// 5. 정적 내부 클래스
public static class Holder{
public static Single INSTANCE;
}
}
public class ClassLoadingDriver {
public static void main(String[] args) {
System.out.println("main");
}
}
결과
ClassLoadingDriver 클래스 로딩, Single 클래스 로딩 X
2. 클래스의 정적 속성을 사용 시 - final X
public static void main(String[] args) {
System.out.println(Single.a);
}
결과
ClassLoadingDriver 클래스 로딩 후, Single 클래스 로딩
3. 클래스의 정적 속성을 사용 시 - final O
public static void main(String[] args) {
System.out.println(Single.b);
}
결과
ClassLoadingDriver 클래스 로딩, Single 클래스 로딩 X
3. 클래스의 정적 메서드를 사용 시
public static void main(String[] args) {
Single.getInstance();
}
결과
ClassLoadingDriver 클래스 로딩 후, Single 클래스 로딩
4. 클래스의 인스턴스를 생성 시
public static void main(String[] args) {
new Single();
}
결과
ClassLoadingDriver 클래스 로딩 후, Single 클래스 로딩
5. 클래스의 정적 내부 클래스의 변수 호출
public static void main(String[] args) {
System.out.println(Single.Holder.INSTANCE);
}
결과
ClassLoadingDriver 클래스 로딩 후, Single 내부의 Holder 클래스 로딩
클래스 초기화
1. 클래스의 정적 변수를 실제 값으로 할당
2.정적 초기화 블록(static initialization block)이 있다면 실행하여 값 할당
이때 클래스의 초기화는 필요한 경우에만 수행하며, 내부의 클래스는 초기화 대상 X
초기화 시점
- 클래스의 인스턴스 생성
- 클래스의 정적 메소드 호출
- 클래스의 정적 변수 할당
- 클래스의 정적 변수 사용 (final x)
사실 위의 클래스 로드 시점과 같으며, 클래스가 로드되면 초기화도 바로 진행된다.
초기화 진행순서
- 정적 블록
- 정적 변수
- 생성자
public class Single2 {
static{
System.out.println("1. 정적 블록");
}
public static Temp temp = new Temp();
public Single2(){
System.out.println("3. 생성자");
}
}
public class Temp{
public Temp(){
System.out.println("2. 정적 변수");
}
}
public class ClassLoadingDriver {
public static void main(String[] args) {
new Single2();
}
}
결과
ClassLoadingDriver 클래스 로딩
Single2 클래스 로딩
정적 블록 실행
Temp 클래스 로딩
정적 변수 실행
생성자 실행
오직 한번만 클래스가 로딩됨을 보장
JLS(Java Language Specification)에 따르면 JVM에 클래스가 로딩되고 초기화될때는 순차적으로 동작함을 보장한다.
멀티 스레드 환경에서 여러개의 스레드가 클래스를 동시에 로딩하려고 시도해도 오직 한개의 클래스만 로딩된다.
public class ThreadSampleDriver {
public static void main(String[] args){
// 1. 스레드 풀 생성
ExecutorService service = Executors.newCachedThreadPool();
// 2. 반복문을 통해 - 10개의 스레드가 동시에 인스턴스 생성
for (int i = 0; i < 10; i++) {
service.submit(() -> {
Single2 instance = new Single2();
System.out.println("instance hashcode :" + instance.hashCode());
});
}
// 3. 종료
service.shutdown();
}
}
위의 코드는 반복문을 통해 10개의 스레드가 동시에 인스턴스를 생성한다.
결과
ThreadSampleDriver 클래스 로딩
Single2 클래스 로딩
정적 블록 실행
Temp 클래스 로딩
정적 변수 실행
생성자 실행
hashcode가 모두 다른 인스턴스 10개
클래스 로딩은 한번만 수행되고, 그때 한번 초기화를 수행한다. 그 후 인스턴스를 10개 생성한다.
이 의미는 멀티 스레드 환경에서 스레드 세이프함을 의미한다.
참고
멘토님
스프링 입문을 위한 자바 객체 지향의 원리와 이해
https://yaboong.github.io/design-pattern/2018/09/28/thread-safe-singleton-patterns/
'JAVA > about java' 카테고리의 다른 글
[variables] 지역변수, 클래스변수, 인스턴스 변수 (0) | 2023.08.20 |
---|---|
[Polymorphism] 다형성과 오버라이딩(overriding) vs 오버로딩(overloading) (0) | 2023.08.18 |
[Thread] 쓰레드 (0) | 2023.05.10 |
[static] satic 블록, static 변수와 static 메서드 (0) | 2023.05.03 |
[Collection] Map (0) | 2023.05.01 |