자바 프로그램이 실행되면 JVM은 OS로부터 메모리를 할당받고, 그 메모리를 용도에 따라서 여러 영역으로 나누어 관리하한다. JVM의 메모리 공간은 크게 Static 영역, Stack영역, Heap 영역으로 구분되는데 Heap영역에 대해서 자세히 알아보도록 하자.
1. Heap 영역
- JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역이다.
- 참조형 데이터 타입을 갖는다 : 값이 저장되어 있는 곳의 주소값을 저장하는 공간이다.
- 클래스
- 인터페이스
- 배열
- 열거
- Heap 영역은 Stack 영역과 다르게 보관되는 메모리가 호출이 끝나더라도 삭제되지 않고 유지된다.
- stack은 스레드 갯수마다 각각 생성되지만, heap은 몇개의 스레드가 존재하든 상관없이 단 하나의 Heap 영역만 존재
2. Java8 이전까지의 Heap 메모리 구조
heap 메모리 영역은 크게 3개의 영역으로 분리되어 있다.
1. Young Generation
- 일단 객체가 생성되면 모두 Eden영역에 먼저 저장된다.
- Eden영역에 데이터가 가득 차게 되면, Eden 영역에 있던 객체가 Survivor1 또는 Survivor2로 옮겨진다.
- Eden 영역에서 Survivor로 옮겨지는 객체들은 어딘가에서 참조되고 있는 객체들이다. 둘 중 하나의 영역이 가득 차게 되면 공간이 남아있는 Survivor로 이동한다. -> 두 Survivor중 하나는 항상 비어있는 공간이 존재한다.
- Young Generation 에서는 1차 GC라고 물리는 Minor GC(Garbage Collection)가 발생한다. Minor GC는 Young Generation에서 사용하지 않는 객체들을 삭제한다.
2. Old Generation
- Survivor1과 Survivor2에서 오랫동안 살아남은 객체들은 Old Generation으로 이동한다. Old 영역은 Young 영역보다 크게 할당하므로 GC가 적게 발생한다.
- Young 영역에서 Old 영역으로 넘어가는 객체중에서 Survivor영역의 크기(16MB)보다 큰 객체의 경우에는 Survivor을 거치지 않고 바로 넘어가기도 한다.
- Old 영역에서는 Major GC 가 일어나는데, 이 때 GC를 진행하는 Thread를 제외하고 다른 Thread는 멈춘 상태로 GC가 일어난다. 이 상태를 Stop-the-world라고 한다.
- 그럼 오랫동안 살아남았다는 것은 어떤 기준일까? 기존 Young 영역에서 Minor GC가 발생할 때마다 살아남은 횟수를 측정하는 age bit값이 1씩 증가하는데 이 값이 MaxTenuringThreshold라는 설정값을 초과하게 되는 경우 Old 영역으로 일어나게 된다.
3. Java8 이후까지의 Heap 메모리 구조
Java8 부터 Permanent 영역의 경우 Metaspace 영역으로 변경되었다. 기존의 Permanent 영역에는 Class의 Meta Data, Method의 Meta Data, Static Object, JVM 관련 데이터가 저장되어 있었다.
하지만 우리가 알기로는 Permanent 영역에 저장되는 Class data, Method data, static object 등은 메소드 영역에 저장되는 거로 알고 있다. 하지만 JDK 8 이후로는 Permanent영역에서 관리하던 것들을 JVM에서 관리하는 Native Memory로 옮기게 되었다. 따라서 Metaspace 영역은 OS에 의해 관리되는 영역이다.
4. GC 동작 원리
그럼 GC(Garbage Collection)이 어떻게 작동하는지 알아보자.
GC는 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체를 모아 주기적으로 제거한다. JVM에서 자동으로 실행되기에 개발자 입장에서 메모리 관리, 메모리 누수 문제에 대해 관리하지 않아도 되어 개발에만 집중할 수 있는 장점이 있다.
- GC 대상
그럼 GC는 어떻게 Garbage로 판단하는 것일까?
GC는 객체가 garbage인지 아닌지 판단하기 위해 Reachability라는 개념을 적용했다. 객체에 레퍼런스가 있다면 Reachable로 구분되고, 객체에 유효한 레퍼런스가 없다면 Unreachable로 구분해버리고 수거해버린다.
- GC 청소 방식
GC를 하기위해서는 Mark-Sweep 방식을 사용한다.
1. Mark 과정 : 먼저 Root Space로 부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
2. Sweep 과정 : 참조하고 있지 않은 객체 즉 Unreachable 객체들을 Heap에서 제거한다.
3. Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다.
이렇게 Mark-Sweep 방식을 사용하면 루트로부터 연결이 끊긴 순환 참조되는 객체들을 모두 지울 수 있다.
'JAVA' 카테고리의 다른 글
[JAVA] 싱글톤 패턴(Singleton Pattern)이란? (0) | 2025.05.20 |
---|---|
[JAVA] 예외클래스와 예외처리 (2) | 2024.12.08 |
[JAVA] 중첩클래스, 중첩인터페이스 (1) | 2024.12.03 |
[JAVA] 디자인패턴 - 데코레이터 패턴(Decorator Pattern)을 알아보자 (0) | 2024.12.03 |
[JAVA] 디자인패턴 - 전략 패턴(Strategy Pattern)을 알아보자 (0) | 2024.12.02 |