본문 바로가기

자바 가비지 컬렉터 (GC)

@정소민fan2025. 9. 7. 22:53

C언어나 C++에서 malloc이나 new로 메모리를 할당해준 뒤, free()나 delete()로 메모리를 해제해본 사람들이 많을것이다.

하지만 일일히 메모리를 해제해주기 상당히 귀찮다 ㅠㅠ

자바에서는 개발자의 귀찮음을 덜고, 메모리 누수를 막기 위해 가비지 컬렉터를 사용해서 자동으로 사용되지 않는 메모리를 해제하여준다.

그렇다면 JVM에서 가비지 컬렉터는 어떻게 동작할까?

가비지 컬렉터의 동작 방식

이전 포스팅에서 런타임 데이터 영역은 5가지 영역으로 나뉘었는데, 이 중 힙 영역에서 가비지 컬렉터가 동작한다.

힙 영역은 크게 young 영역과 old 영역으로 나눌수 있다.

자바 8 버전 이전에는 두 영역 말고도 permount 영역이 있었다. 하지만 이제는 없어지고 네이티브 메모리 영역의 Metaspace 영역으로 대체되었다.

young 영역은 다시 eden, survivor0, survivor1으로 나뉜다. 맨 처음 객체가 생성되면, eden 영역에 할당된다.

이렇게 귀여운 객체들이 eden 영역에 생성되었다. 그런데 eden 영역이 꽉 차서 더 이상 객체를 생성할수가 없는 상황이 온다면 Minor GC가 발생하게 된다.

Minor GC 에서는 young 영역에 있는 객체들을 Mark and Sweep 방식을 통해 더 이상 참조되지 않는지, 아직 참조 중인지 확인하고, 더 이상 참조되지 않는 객체들을 모두 삭제하고, 살아남은 객체들의 age를 1씩 증가시켜서 survivor 영역으로 이동시킨다.

survivor 영역은 0과 1이 있는데, 둘중 비어있는 영역으로 이동시킨다. 지금은 0과 1 모두 비어있는데, 0으로 이동시켜보자.

위 그림에서 빨간색 객체들은 더 이상 참조되지 않아 Minor GC에서 삭제된 객체들이다. 살아남은 객체들은 age가 0에서 1로 증가하고, survivor0으로 이동했다.

그러면 이제 eden 영역은 깔끔하게 비워졌고, survivor0 영역에는 살아남은 객체들이 남아있다.여기서 eden이 다시 한번 꽉 차버렸다고 가정하자. 그러면 다시 Minor GC가 발생할 것이다.

이 떄는 survivor1 영역이 비어있었으므로 eden 의 살아남은 객체가 1 영역으로 이동한다. 또한, 다른 survivor 영역의 객체들이 이번 gc에도 살아남았다면 이 또한 1 영역으로 이동한다. 이 gc로 eden 영역과 0 영역의 객체들은 모두 비워졌다.

이번에는 survivor0 영역이 비었다. 그러면 다시 객체가 생성되면서 eden 영역이 full이 되면? 이번에는 survivor0 영역으로 young 영역에서 살아남은 모든 객체들이 움직일 것이다. 이렇게 young 영역의 살아남은 객체들은 살아남을 때마다 survivor 0 또는 1 둘 중 비어있는 영역으로 움직인다.

 

그런데 이렇게 Minor GC를 맞고도 계속 살아남는다면? 어떻게 될까?

객체의 나이는 계속해서 올라갈 것이고, 만약 어떠한 임계치를 넘는다면, old 영역으로 이 객체는 이동된다. (survivor 영역이 full이 되어버렸을때도 old 영역으로 올라가는데, 이를 조기 승격이라고 한다)

위처럼 eden 영역에 또 객체가 가득 찼다. 이번에는 survivor 0 영역으로 이동할 차례인것 같다. 그런데, 1 영역의 객체 하나가 나이가 좀 많다. old 승격 임계치가 10이라고 가정하고 다음 그림을 보자.

그러면 이 객체는 old 영역으로 이동한다 !!

old 영역으로 이동한 객체는 Minor GC의 영향을 받지 않는다. 오직 Major GC가 일어났을때만 삭제된다.

왜 이렇게 나누어두었을까?

만약 old 객체들도 이동해둔 eden이 꽉 찼을 때 발생하는 GC의 영향을 받는다면, 가비지 컬렉터는 힙 영역 전체를 뒤져야한다.

그러면 당연히 오버헤드가 너무 클 것이다 !! (이 오버헤드를 Stop the World라 한다)

 

그래서 가비지 컬렉터는 Weak Generational 가설에 기반하여 만들어졌다. 이 가설은 대부분의 응용프로그램의 객체들은 생성된 직후, 아주 짧은 시간동안만 사용되고 버려진다는 가설이다. 실제로 90%이상의 객체는 생성된 직후 첫번째 GC에서 삭제된다고 한다. 

 

그러면 old 영역을 매번 GC마다 뒤져볼 필요가 있을까? 당연히 그럴 필요 없다. 이것이 바로 GC가 minor와 major로 나뉘어 있는 이유이다.

 

그러면 Major GC는 언제 일어나는가? 그건 당연히 old 영역이 꽉 찼을 때고, 이 때의 오버헤드는 Minor GC의 10배를 넘는다고 한다.

 

하지만 위 힙 구조는 자바 9부터는 달라졌다. G1 GC를 기본으로 채택하기 때문인데, 이 구조에서는 각 영역이 하나의 덩어리로 만들어져있지 않고, 여러개의 작은 Region으로 쪼개져 역할을 부여받는다고 한다. 이 정책은 조금 더 공부해서 포스팅하겠다.

'Java' 카테고리의 다른 글

JVM 튜?닝?  (1) 2025.11.27
JVM  (0) 2025.09.06
SOLID 원칙  (0) 2025.09.05
와일드카드<?>가 무엇인가?  (0) 2025.08.23
Supplier는 왜 쓰는걸까?  (0) 2025.08.16
정소민fan
@정소민fan :: 코딩은 관성이야

코딩은 관성적으로 해야합니다 즐거운 코딩 되세요

목차