📝느낀 점
처음부터 전체적으로 JVM에 대해 정리를 해보았다. 확실하게 말할 수 있는 것은 이렇게 정리하고 보니 자바가 어떤식으로 실행되고, 어떤식으로 내부에서 흘러가게 되는지, 또한 앞으로는 이제 어떤식으로 코드를 작성해야 메모리 부분에서 더 효율적으로 작성할 수 있게 되었는지도 알게 되었다. 즉, 문자열 같은 경우 '+' 연산자 대신 StringBuilder 를 사용할 경우의 이점들? 힙 영역안에서의 String Pool 을 생각하면서 이런부분에 대해서도 확실히 알게 되어 jvm에 대해서 자세히 공부하길 잘했다는 생각이 든다. 항상 JVM에 대해 자세히는 모르지만 일단 알고 있어야 하는 존재? 라고만 생각하고 있었는데, 오히려 이렇게 알고 나니 신기하기도 하고 사용하고 있는 언어인 자바가 이런식으로 작동하는구나 하며, 자바에 대한 이해도가 한층 더 높아졌단 생각을 스스로도 하게 되는 계기가 되었지 않나 싶다. 더 일찍 공부해볼걸 이란 생각도 든다. 솔직하게 JVM에 대해 이렇게 공부하고 보니, 깊이 학습 한다는 재미를 이제서야 좀 느낄수 었었던 계기가 되지 않았나 싶다. 앞으로 더 재밌게 공부하며, 프로그래밍 하는 생활을 보내고 싶다.
JVM, 자바 가상머신은 단순하게 말하면 컴파일 된 코드(바이트코드)를
실행시켜주는 가상의 컴퓨터라고 생각하면 편하다.
어딜 가든간에 현금 뽑으려면 'ATM' 찾듯이 ->
어느 컴퓨터에서든 바이트코드를 돌리려면 'JVM'을 찾으면 된다
자바는 JVM에 의해 실행 된다
즉, 운영체제에 상관없이 실행 될 수 있고, 가비지 컬렉터(GC)로 메모리 관리를 자동으로 관리 해준다는 점에서
안정적인 프로그래밍을 할 수 있다는 것이다 우선, 스레드마다 PC Register, JVM stack, Native Method stack이 있고,
공통 스레드로는 Heap 영역과 class 영역이 있다.
JVM 원리
첫번째)
자바 소스 코드는 → .java 형태로 저장이 된다.
public A {
public static void main(String[] args) {
// ... 생략
}
}
두번째)
위, 자바 소스 파일을 자바 컴파일러가 javac.exe 명령어를 실행 시켜,
바이트코드로 변경해주는데, 그것이 바로 → .class 파일로 저장된다
바이트코드로 변환하는 이유는?
-> 작성한 코드를 1차적으로 숨기는 이유도 있을 것이고,
바이트코드로 변경하였으니, 문법 검사와 같은 작업을 이후에는
하지 않게 되면서, 시간을 단축시키는 의미도 있다고 한다
세번째)
클래스 파일(바이트코드를 말함)들은 Class Loader가 JVM 메모리 영역인, → Runtime Data Area로 로딩
시키게 되는 것이다. 클래스 로더는 바이트 코드를 읽어서 JVM의 실행 엔진이 사용할 수 있도록 메모리 영역인
Runtime Data Area로 적재한다
바이트코드를 읽고, 클래스 정보를 메모리의 Heap/Method Area에 저장하는 곳
Class Loader의 내부는 어떠한가?
- Loading
- 프로그램을 실행시키기 위해 필요한 class를 찾기 위한 동작
- Linking
- JVM의 메모리에 로드 하기 위해 연결(link) 한다.
- Initialization
- class variable을 초기화하는 역할도 수행
클래스를 메모리에 올리는 로딩 기능은 메모리에 올리지 않고,
어플리케이션에서 필요한 경우 동적으로 메모리에 적재하게 되는 것이다
이런 클래스 파일의 로딩은 Loading → Linking → Initialization인 3단계들을 거쳐서
Runtime Data Area 로 넘어가게 되는 것이다.
네번째)
Runtime Data Area 영역은 5가지 영역이 있다
크게 설명하면 이렇다.
- Metaspace, Heap : 모든 스레드가 공유되고 있다
- Stack, PC Register, Native Method Stack : 스레드마다 하나씩 생성되는 공간이다
Metaspace 영역
JVM이 시작될 때, 생성되는 공간으로 바이트코드가 이 영역에 저장된다
- 클래스 정보, 변수 정보, static으로 선언한 변수가 저장된다.
- 모든 스레드가 공유하는 영역이다.
Metaspace 에는 별도의 영역이 존재한다.
- Runtime Constant Pool이라는 별도의 영역이 존재
- 상수 자료형을 저장하여 참조하는 역할을 한다
저장되는 정보의 종류는?
- Field Info : 멤버 변수의 이름, 데이터 타입, 접근 제어자의 정보
- Method Info : 메소드 이름, Return 타입, 매개변수, 접근 제어자의 정보
- Type Info : Class인지 Interface인지 여부 저장, Type의 속성, 이름, Super Class의 이름
Heap 영역
- Eden 영역 : 새롭게 생성된 객체들이 할당되는 영역
- Eden 영역이 꽉 차면 survival 영역으로 넘어가게 된다.
- Survival 영역(S0, S1) : minor gc로부터 살아남은 객체들이 존재하는 영역
- Survival 영역의 특별한 특징 : Survival 0, 혹은 Survival 1 둘 중 하나는 꼭 비어 있어야 한다는 것이다
Heap 영역은
동적으로 생성된 객체가 저장되는 영역으로 GC의 대상이 되는 공간이다
- new 연산자로 생성된 모든 Object와 인스턴스 변수, 그리고 배열을 저장한다
Heap 영역은 두 영역으로 구분할 수 있다
- Young Generation
- - 생명 주기가 짧은 객체를 `GC` 대상으로 하는 영역 여기 객체들은 `Eden`이라는 곳에 할당이 된 후에,
`Survivor 0`과 `1`을 거쳐, 오래 사용되었다고 판단된 객체들은 `Old Generation`으로 이동하게 되는 것이다
- - 생명 주기가 짧은 객체를 `GC` 대상으로 하는 영역 여기 객체들은 `Eden`이라는 곳에 할당이 된 후에,
- Old Generation
- 생명 주기가 긴 객체를 GC 대상으로 하는 영역
Stack 영역
Stack 영역은 지역변수나 메서드의 매개변수, 임시적으로 사용되는 변수, 메서드의 정보가 저장되는 영역이다
- 지역변수와, 매개변수의 특성상 해당 메서드의 호출이 종료되면 사라진다
- 그렇기에, 금방 사용되고, 사라지는 데이터들을 저장하는 공간이다
PC Register 영역
스레드가 시작될 때, 생성되며 현재 수행 중인 JVM의 명령어 주소를 저장하는 공간이다
- 즉, 스레드가 어떤 부분을 어떤 명령어로 수행할 지를 저장하는 공간이다
Native Method Stack 영역
Java가 아닌 다른 언어인 c, c++ 같은 코드를 위한 공간이다
- 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행되는 영역이다
각 스레드 별로 생성된다는 특징이 있다
다섯번째)
Execution Engine 이 무엇인가?
바이트 코드를 네이티브 코드로 변환 시켜주고, GC를 실행하는 실행 엔진
"클래스 로드로 부터 로드된 '.class' 파일의 바이트코드를 실행하는 엔진인 것이다"
바이트코드를 실행 시키기 위해서는?
바이트코드를 컴퓨터가 이해할 수 있도록 기계어로 바꿔주는 작업이 필요한데,
Execution Engine는 두가지 방법이 사용한다.
- Interpreter : 명령어를 한줄 한줄 해석하면서 실행한다
- JIT Complier : Interpreter 의 단점을 해결하기 위한 방법으로
- 런타임 시간에 한꺼번에 변경하여 실행한다
- Garbage Collector : 더 이상 참조되지 않는 메모리 객체를 모아 제거하는 역할을 수행
여섯번째)
Garbage Collector 설명
가비지 컬렉터(GC)로 메모리 관리를 자동으로 관리 해준다는 점에서 안정적인 프로그래밍을 할 수 있다는 것이다
크게 두가지로 나눌 수 있다
- Minor GC
- Heap 영역의 Young Generation에서 발생하는 GC를 말한다
- Major GC
- Heap 영역의 Old Generation에서 발생하는 GC를 말한다
앞으로 사용되지 않는 객체의 메모리를 'Garbage'라고 부른다
- 이런 'Garbage'를 정해진 스케줄에 의해 정리해주는 것을 'GC'라 부른다
GC가 왜 필요하지?
GC는 메모리 관리 기법 중 하나이다.
프로그램이 "동적으로 할당했던 메모리 영역" 중 "필요 없게 된 영역"을 알아서 해제한다
위의 “동적으로 할당했던 메모리 영역” 은 → Heap 영역 메모리를 뜻하고
“필요 없게 된 영역” 은 → 어떤 변수도 가리키지 않게 된 영역을 의미한다
GC의 장점은?
- 개발자의 실수로 인한 메모리 누수를 막을 수 있다
- 해제된 메모리에 접근하는 오류를 막을 수 있다
- 해제된 메모리를 또 해제하는 이중 해제 또한 막을 수 있다
GC의 단점은?
- GC 작업은 순수 오버헤드
- 개발자는 언제 GC가 메모리를 해제하는지 모른다
'GC' 를 제대로 이해하기 위해서는?
‘Stop The World’ 를 이해 해야 한다.
- GC를 수행하기 위해 JVM이 멈추는 현상을 말한다
- GC가 작동하는 동안 GC관련 Thread를 제외한 모든 Thread는 멈춘다
- 일반적으로 ‘튜닝’이라는 것은 이 시간을 최소화하는 것을 의미한다
모든 쓰레드들의 작업을 중단 후 (Stop The World 과정)
사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업이 재개됩니다.
GC의 종류로는?
- Serial GC
- Parallel GC → 자바 8버전에서 사용
- CMS GC
- G1 GC → 자바 9버전, 10버전 부터 사용 (디폴트)
- Z GC
그럼, 이중에서 뭘 선택해야 하는데?
- 나의 어플리케이션에 서비스 특성에 맞는 GC를 선택하는것이 옳다
Reference :
'Java' 카테고리의 다른 글
컬렉션 프레임워크 설명 (0) | 2023.11.07 |
---|---|
JIT 컴파일러는 무엇인가요? (0) | 2023.11.07 |
배열의 선언과 메모리 할당 (0) | 2023.11.07 |
static primitive, static reference 저장 / static 메서드와 일반 메서드의 차이점 (0) | 2023.11.07 |
exception VS error 차이가 무엇인가요? (0) | 2023.11.07 |