본문 바로가기

Java

JVM & GC

JVM이란?

JVM은 자바 가상 머신으로 자바와 OS사이의 중개자 역할을 한다. 따라서 자바가 OS에 구애받지 않고 작동할 수 있게 해주는 역할을 한다.

JVM의 메모리 구조

JVM이 작동하는 과정을 알아보기 전에 JVM이 어떤 메모리 구조를 가졌는지 먼저 확인한 후 작동 과정을 이해해 보도록 하자.

우선 JVM은 다음 그림과 같은 메모리 구조를 갖는다.

 

각각 하나씩 살펴보도록 하자

 

(1) Class Loader

이름과 같이 JVM 내로 클래스 파일을 로드하는 역할을 한다.  런타임 시에 동적으로 클래스를 로드한다.

 

(2) Execution Engine

클래스 로더를 통해 JVM 내의 Runtime Data Area에 배치된 바이트 코드들을 명렁어 단위로 읽어서 실행해주는 역할을 한다. 

 

 

(3) Garbage Collector

Garbage Collector(GC)는 힙 메모리 영역을 관리해 주는 역할을 한다. 사용하지 않는 객체들을 삭제해준다.

 

 

(4) Runtime Data Area

JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다. 이 영역은 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack로 나눌 수 있다.

 

동작과정

위에서 JVM의 메모리 구조에 대해서 살펴봤다. 이를 바탕을 동작과정을 말하자면, 먼저 JVM은 OS로 부터 필요한 만큼의 메모리를 할당받으며 소스파일이 클래스파일(바이트코드)로 변환이 된다.  그 후에 Class Loader가 Runtime Data Areas의 Method Area에 클래스 파일을 jvm에을 로드 시켜준다. 이후 Method Area에 올라온 class파일을 execution engine이 실행시켜준다.

 

JIT Compiler

이때 Execution Engine은 두 가지의 방식을 혼합하여 실행되는데 우선 Interpreter방식이다.
Interpreter방식은 바이트 코드를 명령어 단위로 읽고 한줄씩 실행하는데 속도가 느리다는 단점이 있다.
그래서 나온 방법이 JIT(Just In Time)컴파일 방식이다. JIT컴파일러는 바이트코드를 os에 맞는 기계코드로 변환 시켜주는 역할을 하면서, 동일한 코드에 대해서는 매번 해석하지 않고 jvm 내의 code cache영역에 캐싱처리를 하여 속도를 향상 시켰다.

  • warm-up
    이러한 JIT 컴파일러의 특징을 이용해 자주 사용하는 클래스를 일부로 호출하여 미리 캐싱하여 성능을 올리는것을 JVM warm-up 이라고 한다.

Runtime Data Area

Runtime Data Area의 메모리에 대해서도 알아보도록 하자

1. Class Area(=Method Area)

  • Method Area, Class Area, Code Area, Static Area
  • 클래스정보(멤버변수의 이름), 변수정보(데이터타입, 접근제어자정보), 메소드정보(메소드 이름, 리턴타입, 파라미터, 접근제어자 정보), static변수, final class변수, Constant pool(상수풀 : 문자상수, 타입, 필드, 객체참조가 저장됨)등을 분류해서 저장한다.
  • JVM이 동작해서 클래스가 로딩될 때 생성

2. Heap Area

  • new 키워드로 생성된 객체와 배열이 저장되는 영역
  • Method Area에 로드된 클래스만 생성이 가능하다.
  • GC의 주요 대상이 된다.
  • 효율적인 GC를 위해 메모리 영역이 분리되어 있다.(Eden, Survivor1,2, Old)
  • 런타임시 할당된다

3. Stack Area

  • 지역변수, 파라미터, 리턴값, 연산에 사용되는 임시값등이 생성되는 영역
  • 메소드를 호출할 때마다 개별적으로 스택이 생성되며 종료 시 영역에서 해제된다.
  • 컴파일 타임 시 할당된다.

4. PC Register

  • 스레드가 생성될때마다 생성되며 현재 스레드가 실행되는 부분의 주소와 명령을 저장하는 영역

5. Native Method Stack

  • 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역(JNI)

Garbage Collector(GC)

가비지 컬렉터란 heap영역의 메모리를 관리시켜주는 것으로 가비지 컬렉션을 수행하는 부분을 말한다. G1 GC를 제외한 JVM은 자바의 힙 영역에서 YOUNG, OLD, PERM으로 나누어진다. PERM영역에는 클래스나 메소드에 관한 내용이 저장되고, YOUNG 영역에는 젊은 객체, OLD 영역에는 늙은 객체들이 저장 된다. 이때 YOUNG 영역은 EDEN과 SURVIVOR영역으로 나뉘는데 객체가 처음 생성될 때는 EDEN영역에 생성되며 EDEN 영역이 꽉찼을때 이중 살아남은 객체가 SURVIVOR영역으로 간다. SURVIVOR영역이 꽉 차게 되면 또 다른 SURVIVOR영역으로 복제가 되며 이중 오래 살아남은 객체는 OLD영역으로 이동한다. Young 영역에서 발생하는 GC를 Young GC(가비지 컬렉션) 또는 Minor GC라고 하며 Heap 영역 전체에서 발생하는 GC를 Full GC 또는 Major GC라고 한다. Full GC는 종류에 따라 알고리즘이 다른데 Serial GC, Parallel Young GC, Concurrent Mark & Sweep Collector (줄여서 CMS), G1 GC가 있다

 

 

Gabage Collection
가비지 컬렉션은 메모리를 비워주는 동작을 말한다. Young 영역에서 발생하는 가비지 컬렉션을 Minor GC Old 영역에서 발생하는 GC를 Major GC라고 한다.

 

Stop the world
가비지 컬렉션이 발생할 때 처리중인 프로그램은 잠시 멈추게 된다. 이를 stop the world라고 한다.

 

Serial GC
Serial GC에서 Old 영역의 GC는 mark-sweep-compact이라는 알고리즘을 사용한다. 이 알고리즘의 첫 단계는 Old 영역에 살아 있는 객체를 식별(Mark)하는 것이다. 그 다음에는 힙(heap)의 앞 부분부터 확인하여 살아 있는 것만 남긴다(Sweep). 마지막 단계에서는 각 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 객체가 없는 부분으로 나눈다(Compaction).
Serial GC는 적은 메모리와 CPU 코어 개수가 적을 때 적합한 방식이다. 따라서 Serial GC는 WAS에서는 사용하면 안된다.

 

ParallelGC
Parallel GC는 Serial GC와 기본적인 알고리즘은 같다. 그러나 Serial GC는 GC를 처리하는 스레드가 하나인 것에 비해, Parallel GC는 GC를 처리하는 쓰레드가 여러 개이다. 그렇기 때문에 Serial GC보다 빠른게 객체를 처리할 수 있다. Parallel GC는 메모리가 충분하고 코어의 개수가 많을 때 유리하다. Parallel GC는 Throughput GC라고도 부른다.

 

Parallel Old GC
Parallel Old GC는 JDK 5 update 6부터 제공한 GC 방식이다. 앞서 설명한 Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다르다. 이 방식은 Mark-Summary-Compaction 단계를 거친다. Summary 단계는 앞서 GC를 수행한 영역에 대해서 별도로 살아 있는 객체를 식별한다는 점에서 Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.

 

CMS GC
초기 Initial Mark 단계에서는 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는 것으로 끝낸다. 따라서, 멈추는 시간은 매우 짧다. 그리고 Concurrent Mark 단계에서는 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다. 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 것이다.

그 다음 Remark 단계에서는 Concurrent Mark 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인한다. 마지막으로 Concurrent Sweep 단계에서는 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되고 있는 상황에서 진행한다.

이러한 단계로 진행되는 GC 방식이기 때문에 stop-the-world 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용하며, Low Latency GC라고도 부른다.

그런데 CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재한다.

  • 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
  • Compaction 단계가 기본적으로 제공되지 않는다.

g1gc
G1 GC는 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다. 그러다가, 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행한다. 즉, 지금까지 설명한 Young의 세가지 영역에서 데이터가 Old 영역으로 이동하는 단계가 사라진 GC 방식이라고 이해하면 된다. G1 GC는 장기적으로 말도 많고 탈도 많은 CMS GC를 대체하기 위해서 만들어 졌다.G1 GC의 가장 큰 장점은 성능이다. 지금까지 설명한 어떤 GC 방식보다도 빠르다. 하지만, JDK 6에서는 G1 GC를 early access라고 부르며 그냥 시험삼아 사용할 수만 있도록 한다. 그리고 JDK 7에서 정식으로 G1 GC를 포함하여 제공한다.

 

자바8버전 부터는 메모리 구조가 조금 바뀌었는데 이는 java8에 대해서 이야기 할 때 알아보도록 하자 

'Java' 카테고리의 다른 글

자바의 Collection  (0) 2023.01.20
if-else 문과 switch, enum  (0) 2023.01.19
자바의 변수와 타입  (0) 2023.01.19
객체지향 프로그래밍  (2) 2022.11.10