티스토리 뷰
JVM 구조
클래스 로더 시스템, 메모리, 실행 엔진, 네이티브 메서드 인터페이스, 네이티브 메소드 라이브러리가 존재한다.
이미지는 다음 링크를 참고하자.
1. 클래스 로더 시스템
1.1 로딩(loading)
클래스 로더가 프로젝트 내에 있는 소스 코드 형태로 되어 있는 .class 파일을 읽는다.
그 파일의 내용을 binary 데이터를 만들고, 메소드에 저장한다.
이 때 저장하는 데이터는 다음과 같다.
- FQCN(Fully Qualified Class Name)
- 그 파일이 클래스인지 인터페이스인지 enum인지에 대한 식별 정보
- 파일 내에 있는 메서드와 프로퍼티 정보
로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 힙에 저장한다.
다음과 같은 Person 클래스가 있다고 해보자.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
이 클래스의 로딩이 끝나면 다음과 같이 Class 객체가 생성되어 사용할 수 있게 된다.
public class Main {
public static void main(String[] args) {
// [1]
System.out.println(Person.class);
// [2]
Person person = new Person("eric", 27);
System.out.println(person.getClass());
// [3]
Class<Person> personClass;
}
}
// 결과
class com.example.kkoon9.kwon.Person
class com.example.kkoon9.kwon.Person
힙과 메서드 영역에 있는 데이터들은 쓰레드에 국한되지 않고, 공용으로 사용될 수 있다.
클래스 로더에는 다음과 같이 세 가지 종류가 존재한다.
- 부트 스트랩
- 플랫폼(前 Extension)
- 애플리케이션
위는 다음 코드를 통해서 확인해볼 수 있다.
public class Main {
public static void main(String[] args) {
ClassLoader classLoader = Person.class.getClassLoader();
System.out.println(classLoader);
System.out.println(classLoader.getParent());
System.out.println(classLoader.getParent().getParent());
}
}
// 실행결과
jdk.internal.loader.ClassLoaders$AppClassLoader@277050dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@cac736f
null
아까 만든 Person 클래스에서 getClassLoader() 메서드를 통해 클래스 로더를 가져올 수 있다.
클래스 로더는 트리 구조를 가지기 때문에 getParent() 메서드를 통해 부모를 가져올 수 있다.
실행 결과를 보면 AppClassLoader, PlatformClassLoader, null이 뜨게 된다.
AppClassLoader는 Application 클래스 로더를 뜻한다.
🤔
마지막 null은 무엇일까?
부트스트랩 클래스 로더는 네이티브로 구현되어 있기 때문에 코드에서는 참조할 수 없다고 한다.
우리가 작성하는 코드는 대부분 AppClassLoader에서 읽게 된다.
🤔
세 가지 클래스 로더에서 우선순위도 존재할까?
물론 존재한다.
제일 부모인 부트스트랩 클래스 로더부터 읽어오고, 만약 읽지 못했다면 그 다음 클래스 로더에서 읽게 된다.
1.2 링크(link)
링크는 다음과 같은 세 단계로 나뉠 수 있다.
- Verify : .class 파일 형식이 유효한지 체크하는 과정이다.
- Prepare : 클래스 변수와 기본값에 필요한 메모리를 준비하는 과정이다.
- Resolve(Optional) : 심볼릭 메서드 레퍼런스를 실제 레퍼런스로 교체하는 과정이다.
1.3 초기화(initialization)
static한 변수들이 이 단계에서 할당이 된다.
2. 메모리
2.1 메서드(method)
클래스 수준의 정보가 저장되며, 위에서 언급한대로 데이터가 다른 쓰레드에도 공유된다.
클래스 수준의 정보는 다음과 같다.
- 클래스 이름
- full package path
- 부모 클래스 이름
- 메서드
- 변수
2.2 힙(heap)
객체를 저장되며, 위에서 언급한대로 데이터가 다른 쓰레드에도 공유된다.
클래스의 인스턴스 역시 이 영역에 저장된다.
2.3 스택(stack)
런타임 스택을 만들고 그 안에 메서드 호출을 콜 스택에 블럭으로 쌓는다.
쓰레드가 종료하면 런타임 스택도 사라진다.
콜 스택은 애플리케이션 내 에러가 발생하면 터미널에 나타나는 로그들을 예시로 들 수 있다.
다른 쓰레드에 공유되지 않고, 그 쓰레드에 국한된다.
2.4 PC 레지스터(Promgram Counter register)
쓰래드 내 다음 명령의 위치를 가리키는 포인터를 의미한다.
다른 쓰레드에 공유되지 않고, 그 쓰레드에 국한된다.
2.5 네이티브 메서드 스택(native method stack)
이름 그대로 네이티브 메서드를 호출할 때 쌓이는 스택을 의미한다.
다른 쓰레드에 공유되지 않고, 그 쓰레드에 국한된다.
3. 네이티브 메서드 인터페이스(JNI), 네이티브 메서드 라이브러리
네이티브 메서드 라이브러리는 native 키워드가 붙어 있으며, C, C++로 작성된 라이브러리를 의미한다.
예시로, Thread의 currentThread() 메서드가 있다.
JNI는 Java Native Interface의 약자로, 자바 애플리케이션에서 위같은 메서드를 사용할 수 있도록 도와준다.
4. 실행 엔진
바이트 코드를 실행시키는 역할을 수행한다.
인터프리터, JIT 컴파일러, GC로 이루어져 있는데, 다른 포스팅에서 자세히 다룰 예정이다.
Java의 실행방식
- 자바 컴파일러가 자바 소스코드(.java)를 읽어 자바 바이트코드(.class)로 변환시킨다.
- 클래스 로더를 통해 클래스 파일들을 JVM으로 로딩한다.
- 로딩된 클래스 파일들은 실행 엔진을 통해 해석된다.
'JAVA' 카테고리의 다른 글
new String()과 ""의 차이점과 불변 클래스 (0) | 2023.02.09 |
---|---|
오브젝트의 동일성과 동등성 (0) | 2022.07.10 |
방어적 복사 (0) | 2022.05.01 |
java에서 멀티쓰레드 [3] Callable과 Future (0) | 2022.02.07 |
java에서 멀티쓰레드 [2] Executors와 ExecutorService (0) | 2022.02.07 |
- Total
- Today
- Yesterday
- node.js
- programmers
- Kotlin
- 이팩티브 자바
- Olympiad
- 정규표현식
- 클린 코드
- BOJ
- 프로그래머스
- kotest
- AWS
- C++
- Spring Boot
- Algorithm
- Effective Java
- 디자인 패턴
- MSA
- Java
- kkoon9
- 디자인패턴
- BAEKJOON
- 객체지향
- 알고리즘
- 이펙티브 자바
- JPA
- 클린 아키텍처
- 코테
- 테라폼
- Spring
- 백준
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |