두's 스토리
JVM과 자바 메모리 본문
JVM 이란
Java Virtual Machine
자바 바이트 코드를 OS에 맞게 해석해주는 역할
.java 파일은 사람이 읽기 쉬운 코드
.class 파일, 즉 바이트 코드(중간코드) = 기계어 x
JVM은 OS가 바이트코드를 이해할 수 있도록 해석해줌
바이트 코드는 JVM 위에서 OS에 상관없이 실행 - 자바의 장점
JVM 구조
JVM 은 크게 Class Loader, Runtime Data Areas, Execution Engine 3가지로 구성
1. Class Loader
- JVM내로 클래스를 로드하고 링크를 통해 배치
- 런타임시 동적으로 클래스를 로드한다.
2. Execution Engine'
- Class Loader를 통해 JVM 내의 런타임 데이터 영역에 배치된 바이트 코드를 실행
- 자바 바이트 코드를 명령어 단위로 읽어서 실행
3. Runtime Data Areas
JVM이 운영체제 위에 실행되면서 할당받는 메모리 영역
Class Loader에서 준비한 데이터들을 보관하는 저장소
자바 메모리 구조
Runtime Data Areas
크게 3가지
Method (Static) Area : 멤버 변수(필드), 클래스 변수(Static 변수), 생성자와 메소드를 저장하는 공간.
클래스 같은 껍데기(컨테이너 저장)
Runtime Constant Pool
1.클래스와 인터페이스 상수, 메소드와 필드에 대한 모든 레퍼런스(주소)를 저장한다. 2.JVM은 런타임 상수 풀을 통해 해당 메소드나 필드의 실제 메모리 상 주소를 찾아 참조한다.
JVM 시작시 생성되어 프로그램 종료 시에 사라짐
모든 스레드에서 공유한다.
Heap Area
- 객체(내용물)를 저장
- JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역.
- New 연산자로 생성된 객체 또는 객체(인스턴스)와 배열을 저장
- 모든 스레드에서 공유한다.
- GC 의 대상이다.
Stack Area
- 각 스레드마다 하나씩 존재, 스레드가 시작될 때 할당
- 메소드를 호출할 때마다 프레임(Frame)을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop) 하는 동작을 수행한다.
- 선입후출(FILO, First In Last Out) 구조
- 메소드 정보, 지역변수, 매개변수, 연산 중 발생하는 임시 데이터 저장
- 기본(원시)타입 변수는 스택 영역에 직접 값을 가진다.
- 참조타입 변수는 힙 영역이나 메소드 영역의 객체 주소를 가진다.
그 외
- PC Register
- 현재 수행 중인 JVM 명령 주소를 갖는다.
- 프로그램 실행은 CPU에서 인스트럭션을 수행
- CPU는 인스트럭션을 수행하는 동안 필요한 정보를 CPU 내 기억장치인 레지스터에 저장한다.
- 연산 결과값을 메모리에 전달하기 전 저장하는 CPU 내의 기억장치
- Native Method Stack Area
- 자바 외 언어로 작성된 네이티브 코드를 위한 Stack이다.
자바 메모리 관리(Heap)
크게 3개 영역
New/Young 영역 : 새로 생성된 객체를 저장
Old 영역 : 만들어진지 오래된 객체를 저장
Permanent 영역 : JVM클래스와 메서드 객체를 저장
New 영역
- Eden : 모든 새로 만들어진 객체를 저장
- Survivor Space 1, Survivor Space 2 : Old 영역으로 넘어가기 전 객체들이 저장되는 공간
Garbage Collector
전통적인 언어의 경우 일일이 메모리를 수거해 줘야 했음.
그러나, GC기술을 사용하여 개발자로 하여금 메모리 관리에서 자유롭게 해줌.
GC는 New/Young 영역과 Old 영역에 대해서만 GC를 수행한다. (Permanent 제외)
Minor GC
New 영역의 GC, New 영역은 Eden과 Survivor라는 두 영역으로 구분
Eden 영역은 자바 객체가 생성 되자 마자 저장이 되는 곳
객체의 Minor GC가 발생할 때 Survivor 영역으로 이동 Survivor 영역은 Survivor1과 Survivor2로 나뉘어 지는데, Minor GC가 발생하면 Eden과 Survivor1에 살아있는 객체가 Survivor2로 이동되고, 결과적으로 현재 살아있는 객체들만 Survivor2에 남아있게 된다. 다음번 Minor GC가 발생되면 같은 원리로, Eden과 Survivor2의 살아있는 객체가 Survivor1으로 이동되고, 두 영역은 Clear. 이와 같은 방법으로 반복되면서 메모리를 수거. (Copy & Scavenge) 장점 - 속도가 빠르며 작은 크기의 메모리를 collecting 하는데 효과적. Minor GC 과정 중 오래된 객체는 Old 영역으로 복사된다.
Full GC
Old 영역의 GC를 Full GC라 한다.
Mark & Compact 알고리즘을 이용.
전체 객체들의 reference를 따라가면서 연결이 끊긴 객체를 marking 한다. 이 작업이 끝나면 사용되지 않는 객체가 모두 mark 되고, 이 객체들을 삭제한다. 실제로는 삭제가 아니라, mark 된 객체로 생기는 부분을 unmark된, 즉 사용중인 객체로 메꾸는 방법 Full GC는 속도가 매우 느리며, Full GC가 일어나는 도중에 순간적으로 java application이 멈춰버리기 때문에 Full GC가 일어나는 정도와 Full GC에 소요되는 시간은 application의 성능과 안정성에 매우 큰 영향을 미치게 된다.
GC 가 중요한 이유
- GC 에 의해서 application이 멈출 경우, 멈춰있는 동안 사용자의 request는 쇄도하게 되고, queue에 저장되었다가 요청이 한꺼번에 들어오게되면 여러 장애를 발생할 수 있게 됨. 따라서 원활한 서비스를 위해서 GC를 어떻게 일어나게 하느냐가 시스템의 안정성과 성능에 변수로 작용하게 된다.
GC 알고리즘들
- Default Collector : 전통적인 GC, Minor GC에 Scavenge를, Full GC에 Mark & Compact를 사용하는 방법.
- Parallel GC : JDK 1.3까지는 하나의 스레드에서만 GC가 수행되었으나 JDK 1.4 부터 minor GC를 동시에 여러개의 thread를 이용하여 수행.
'프로그래밍언어' 카테고리의 다른 글
객체 지향 프로그래밍(OOP) (0) | 2020.01.28 |
---|---|
C++ 자료형 정리 (0) | 2019.09.12 |