GC(Garbage Collection)
- 메모리 관리는 컴파일러나 런타임의 영역
- 런타임이 대신 객체를 추적하여 쓸모없는 객체를 알아서 제거해준다
- 자동 회수한 메모리는 깨끗이 비우고 재활용 할 수 있다
GC의 기본원칙
- 알고리즘은 반드시 모든 가비지를 수집해야한다
- 살아있는 객체는 절대로 수집하면 안된다 (중요🌟)
Mark and Sweep 알고리즘
- GC의 기초 알고리즘이다
- 할당됐지만, 아직 회수되지 않은 객체를 가리키는 포인터를 포함한 할당 리스트를 사용한다
- 할당 리스트를 순회하며 마크비트를 지운다
- GC 루트부터 살아있는 객체를 찾는다
- 찾은 객체마다 마크비트를 세팅한다
- 마크비트가 세팅되지 않은 객체를 찾는다
4-1. 힙에서 메모리를 회수해 프리 리스트에 되돌린다
4-2. 할당 리스트에서 객체를 삭제한다
GC 용어
STW(Stop the world)
- GC중에는 모든 application thread가 중단된다
동시
- GC thread는 application thread와 동시 실행될 수 있다
- 비싼 작업인데다, 실상 100% 동시 실행을 보장하는 알고리즘은 없음
병렬
정확
- GC 스킴(계획, 전략)은 전체 가비지를 한방에 수집할 수 있도록 힙 상태에 관한 충분한 타입 정보를 지니고 있다
보수
- 보수적인 스킴은 정보가 없다
- 리소스를 낭비하는 일이 잦기 때문에 훨씬 비효율적이다
이동
- 이동 수집기에서 객체는 메모리를 여기저기 오갈 수 있다 (객체 주소가 고정된게 아니다)
압착
- 객체 쓰기가 가능한 여백의 시작점을 가리키는 포인터가 있다
방출
- 수집 사이클 마지막에 살아남은 객체는 모두 다른 메모리 영역으로 이동한다
핫스팟 런타임
객체를 런타임에 표현하기
- 핫스팟은 런타임에
oop 라는 구조체로 자바 객체를 나타낸다
oop는 ordinary object pointer의 줄임말
- 객체의 위치 정보나 메타정보를 담고있는 단위이다
- instance가 생성될 때 마다, instance 정보가 담긴
oop가 생성된다
instanceOop
- instanceOop는 자바 클래스의 인스턴스를 나타낸다
instanceOop의 메모리 레이아웃
- 객체에 대한 메타정보를 저장한다
- header + 실제데이터로 구성되어 있다
- header는 Mark 워드 + Klass 워드 로 구성된다(둘다 기계어 워드)
klassOop
instanceOop와 klassOop

(출처: Java 객체는 어떻게 이루어져 있을까?)
- 왼쪽은 객체의 정보를 담은 instance oop, 오른쪽은 class 정보를 담은 klass oop이다
- instance oop는 객체 정보(Klass word) + Mark word + 데이터 로 구성되어있다
- klass oop는 실제 클래스가 가지고 있는 메서드 등의 정보가 담겨있다
GC 루트 및 아레나
GC 루트의 종류
스택 프레임, JNI, 레지스터, 코드 루트, 전역 객체, 로드된 클래스의 메타데이터
-
heap에 있는 객체를 가리키는 참조형 지역변수도 말하자면 가장 단순한 형태의 GC루트이다
-
핫스팟 GC는 아레나(무대)라는 메모리 영역에서 작동한다
-
핫스팟은 java heap을 관리할 때 시스템 콜을 하지 않는다 (중요 🌟)
- Java heap을 영역에 대한 메모리를 할당하고 관리할 때, 운영체제 수준의 시스템 콜을 최소화한다는 의미
- 핫스팟 JVM은 초기에 heap을 영역에 대한 큰 덩어리의 메모리를 운영체제로부터 할당받는다. 이후 Java 프로그램이 실행되면서 객체를 생성할 때, 핫스팟 JVM은 이미 할당받은 heap을 메모리 내에서 필요한 공간을 할당하고 관리한다. 이 방식으로, JVM은 Java 애플리케이션 실행 중에 객체를 생성하고 삭제하는 과정에서 발생하는 메모리 할당 요청을 처리할 때 매번 시스템 콜을 수행하지 않고, 미리 할당받은 메모리 영역 내에서 작업을 처리할 수 있다. (by. chatgpt)
➡️ 핫스팟은 유저 공간 코드에서 heap을 크기를 관리하므로 단순 측정값을 이용해 GC 서브시스템이 어떤 성능 문제를 일으키고 있는지 파악할 수 있다
할당과 수명
GC가 일어나는 주된 원인?
➡️ 할당률, 객체수명
GC는 "메모리를 회수해 재사용"하는 일이다. 객체는 대부분 단명하므로 GC의 핵심 전제는 동일한 물리 메모리 조각을 몇번이고 계속 다시 쓸 수 있는가 하는 점이다
할당률
- 일정기간 새로 생성된 객체가 사용한 메모리량
- JVM이 직접 기록하진 않지만, 비교적 쉽게 측정가능한 값이다 (센섬같은 툴을 쓰면 된다)
객체수명
측정하기 어렵다 ➡️ 핵심요인이다🌟
가설
객체의 수명과 가비지 컬렉션(Garbage Collection, GC)의 효율성에 관한 가설
약한 세대별 가설
- 시스템의 런타임 작용을 관찰
- 거의 대부분의 객체는 아주 짧은 시간만 살아있지만, 나머지 객체는 기대수명이 훨씬 길다
➡️ 단명 객체를 쉽고 빠르게 수집할 수 있게 설계해야 하고, 장수 객체와 단명 객체를 완전히 떼어놓는게 가장 좋다
🌟객체를 세대에 따라 분류하고, 각 세대별로 다른 방식으로 메모리를 관리한다🌟

- 객체마다 세대 카운트를 센다
- 세대 카운트란, 객체가 지금까지 무사 통과한 GC 횟수이다
- 큰 객체를 제외한 나머지만 에덴 공간에 생성한다. 여기서 살아남은 객체는 다른 곳으로 옮긴다
- 젊은 세대(Young Generation)의 일부
- GC 발생 시, 에덴 공간에서 살아남은 객체들은 "서바이버(Survivor) 공간"으로 옮겨진다
- 장수했다고 할 정도로 오래 살아남은 객체들은 별도의 메모리영역(올드 or 테뉴어드)에 보관한다
- 장수 했다는 것은, 여러번의 GC 수행에도 살아남은 것이다
- 이곳에 옮겨진 객체들은 GC가 덜 자주 발생한다. 이는 메모리 관리의 효율성을 높이기 위한 조치이다
에덴 공간
- 에덴은 대부분의 객체가 탄생하는 장소이고, 단명객체는 다른 곳에 위치할 수 없으므로 특별히 관리를 잘해야 한다
할당
GC 프로세스는 메모리가 부족할 때(heap 메모리 공간이 꽉 채워져 더이상 객체를 생성할 공간이 없을 때) 그때그때 필요에 의해 발생한다
➡️ 예측 가능하지 않다. 불확정적이다. 규칙성이 없다
- 할당률(일정기간 새로 생성된 객체가 사용한 메모리량)이 높으면 GC는 더 자주 발생한다
- 할당률이 높다는 것은 이 영역에 많은 객체가 할당되어 있어 사용 가능한 메모리 공간이 적다는 것을 의미한다
- 메모리가 가득 차게 되면, 새로운 객체를 위한 공간을 만들기 위해, JVM은 사용되지 않는 객체들을 제거해야 하므로 GC가 자주 발생한다
- 할당률이 너무 높으면 객체는 테뉴어드로 곧장 승격된다 ➡️ "조기승격" 이라고 한다
- 할당률이 높으면 에덴 공간과 서바이버 공간에 더 이상 새 객체를 저장할 공간이 없거나 부족하기 때문이다