1. JVM(Java Virtual Machine)
- JVM은 자바 프로그램 실행환경을 만들어주는 소프트웨어이며, 메모리 관리(GC)를 수행하며 스택 기반의 가상머신이다.
- JVM은 자바 가상 머신의 약자로, 자바 애플리케이션을 클래스 로더를 통해 읽어 자바 API와 함께 실행할 수 있도록 한다.
- 자바 코드를 컴파일하여 .class의 바이트 코드로 만들면 이코드가 자바 가상 머신 환경에서 실행된다.
- JVM은 자바 실행환경 JRE(Java Runtime Environment)에 포함되어 있으며 현재 사용하는 운영체제에 맞춰 자바 실행환경 (JRE)가 설치되어 있다면 자바 가상머신이 설치된 것이다.
- JVM을 통해 하나의 바이트 코드(.class)를 모든 플랫폼(운영체제)에서 동작이 가능하다.
- 따라서 Java는 JVM을 통해 플랫폼에 종속적이지 않지만, JVM은은 플랫폼에 의존적이라서 각 플랫폼에 맞는 JVM이 존재한다.
1) JVM의 구조
- JVM의 구조는 Class Loader, Execution engine, Runtime Data Area, JNI, Native Method Library로 이루어져 있다.
- Class Lodaer : JVM 내의 클래스를 로드하고 링크를 통해 배치하는 작업을 수행하는 모듈
- Execution engine : 바이트 코드를 실행시키는 역할
- 인터프리터 : 바이트 코드를 한 줄씩 실행한다.
- JIT 컴파일러 : 인터프리터의 효율을 높이기 위한 컴파일러로 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러가 반복되는 코드를 네이티브 코드로 바꿔준다. 그 다음에 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용한다.
- GC(Garbage Collector) : 가비지 컬렉터로 힙 영역에서 사용되지 않은 객체들을 제거하는 작업을 의미한다.
- Runtime Data Areas : 프로그램 실행 중에 사용되는 다양한 영역이다.
- PC Register : Thread가 시작될 때 생성되며 현재 수행 중인 JVM의 명령어 주소를 가지고 있다.
- Stack Area : 지역 변수, 파라미터 등이 생성되는 영역이며 실제 객체는 Heap에 할당되고 해당 레퍼런스만 Stack에 저장된다.
- Heap Area : 동적으로 생성된 오브젝트와 배열이 저장되는 곳으로 GC의 대상 영역이다.
- Method Area : 클래스 멤버 변수, 메소드 정보, Type 정보, Constant Pool, static, final 변수 등이 생성된다. 상수 풀(Constant Pool)은 모든 Symbolic Reference를 포함하고 있다.
- JNI(Java Native Interface) : 자바 애플리케이션에서 C, C++, 어셈블리어로 작성된 함수를 사용할 수 있는 방법을 제공한다. Native 키워드를 사용해 메서드를 호출하며 대표적인 메서드는 Thread의 currentThread()이다.
- Native Method Library : C, C++로 작성된 라이브러리다. 자바 이외의 언어로 작성된 네이티브 코드를 실행할 때 사용되는 메모리 영역으로 일반적인 C스택을 사용한다.
2) JAVA의 실행 방식
1. 자바로 개발된 프로그램을 실행하면 JVM은 OS로 부터 메모리를 할당한다.
2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일 한다.
3. Class Loader를 통해 JVM Runtime Data Area로 로딩한다.
4. Runtime Data Area 에 로딩된 .class는 Execution Engine을 통해 해석한다.
5. 해석된 바이트 코드는 Runtime Data Area의 각 영역에 배치되어 수행되며, 이 과정에서 Execution engine에 의해 GC 작동과 스레드의 동기화가 이루어진다.
2. GC(Garbage Collector)
1) GC란 ?
- Garbage Collector는 힙 영역에서 사용하지 않은 객체들을 제거하는 작업이다.
- 이 객체를 제거하는 작업이 푤이한 이유는 자바는 개발자가 메모리를 직접 해제할 수 없는 언어이기 때문이며 객체를 사용하고 제거하는 기능이 반드시 필요하다.
- JVM의 Heap 영역에서 동적으로 할당됐던 메모리 영역 중에 필요 없어진 메모리 영역을 주기적으로 제거한다.
2) GC의 단점
- 개발자가 메모리가 언제 해제되는지를 정확하게 알 수 없다.
- 가비지 컬렉션이 동작하는 동안에 다른 동작이나 JVM을 멈추기 때문에 성능상 오버헤드가 발생한다.
3) Heap 영역(Heap Area)
- Heap 영역은 효율적인 GC를 위해 위와 같이 3가지 영역으로 나뉜다.
- Young Generation : 자바 객체가 생성되자마자 저장되고 생긴지 얼마 안된 객체들이 저장되는 공간이다. Heap 영역에 객체가 생성되면 최초로 Eden 영역에 할당되며 이 영역에서 어느정도 데이터가 쌓이게 되면 참조되는 정도에 따라 Servivor의 빈공간으로 이동되거나 회수가 이루어진다.
- Young Generation(Eden + Servivor) 영역이 차면 또 참조 정도에 따라 Old 영역으로 이동되거나 회수가 이루어진다. 이렇게 Young Generation과 Tenured Generation에서의 GC를 Minor GC라고 하며 Old 영역에 할당된 메모리가 허용치를 넘어 가면 Old 영역의 있는 모든 객체들을 검사해 참조되지 않은 객체를 한꺼번에 삭제하는 GC가 동작된다. 이때 시간이 오래 걸리는 작업이므로 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들은 작업을 멈추게 된다.
- 이것을 'Stop-the-World'라고 하며 멈추게 되면 Old 영역의 메모리 회수하는 GC를 Major GC라고 한다.
3) Heap 영역(Heap Area)에서 GC
- GC는 Minor GC, Major GC로 구분할 수 있으며 Minor GC는 young영역에서, Major GC는 old영역에서 일어난다.
- Minor GC는 Eden 영역이 가득 차면 시작되며, Eden 영역에서 참조가 남아있는 객체를 Mark하고 Survivor 영역으로 복사한다. 그리고 Eden 영역을 비우게 된다. Survivor 영역도 가득 차면 같은 방식으로 다른 Survivor 영역에 복사하고 비우며 이를 계속 반복하다 보면 계속해서 살아남는 객체들을 Old 영역으로 이동하게 된다.
- Major GC는 Old 영역에서 일어나며 위와 반대로 삭제되어야 하는 객체를 Mark 한다. 그리고 지우는(Sweep) 작업을 진행한다. 메모리는 단편화된 상태이므로 이를 한 군데에 모아주는 것을 Compaction이라고 하며 compact라고도 부른다. 그래서 Mark-Sweep-Compact 알고리즘을 수행한다.
Mark-Sweep-Compact 알고리즘
- Mark-Sweep-Compact 알고리즘은 가비지 컬렉션이 동작하는 원리로 루트에서부터 해당 객체에 접근 간읗나지에 대한 여부의 기준으로 메모리를 해제한다. 아래와 같이 총 3가지 과정을 통해 진행된다.
- Mark 과정 : 먼저 Root로 부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지를 찾으며 마킹을 한다.
- Sweep 과정 : 참조하고 있찌 않은 객체 즉 Unreachable 객체들을 Heap에서 제거한다.
- Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다. 그러나 가비지 컬렉션의 종류에 따라 하지 않을 경우도 있다.
이런 GC의 동작들이 중요한 이유는 GC 을 수행하면 시스템이 멈추기 떄문에 의도치 않은 장애의 원인이 될 수 있으며 따라서 이를 위해 힙영역을 조정하는 것을 GC 튜닝이라고 하며 JVM 메모리는 절대로 마음대로 조정해서 안된다.
** 참조
(https://coding-factory.tistory.com/829)
(https://github.com/ksundong/backend-interview-question)
공부를 진행하면서 참조하며 작성하였으며 문제가 있을 시에 바로 삭제하겠습니다.
'프로그래밍공부 > JAVA' 카테고리의 다른 글
JAVA - 변수 (0) | 2021.10.01 |
---|---|
JAVA 언어에 대하여 (0) | 2021.09.03 |