loading
본문 바로가기
Language/go lang.

[Go]GO 가비지 컬렉션(Garbage Collection)

by 개발자 김모씨 2020. 9. 11.
반응형

저번 포스팅에 이어,
Go의 대표적인 특징 중 하나인 가비지 컬렉션에 대해 알아보자.

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

 

가비지 컬렉터(GC)란?

GO 언어의 이야기를 하기에 앞서,
먼저 가비지 컬렉터가 무엇인지 알아야한다.

여기서 가비지란, '유효하지 않은 메모리 주소', '해제되지 않은 메모리 영역'를 의미한다.
프로그래밍 언어에서는 보통 Danling Object라고 불리며,
Java나 Go 에서는 Garbage라는 용어를 사용하고 있다.


우리에게 가장 친숙한 C/C++에서 그 예를 찾아보자면,

//c++

int main(void)
{
	int *a = new int;

	return 0;	//Pointer를 해제하지 않고 프로그램 종료 : 메모리 누수 발생
}

int* 에 할당된 메모리를 해제 하지 않고 프로그램을 종료하였다.
C/C++에는 별도의 가비지 컬렉터가 없다.
그래서 사용자가 직접 동적 메모리를 적절한 위치와 타이밍에 해제해주어야 한다.
이렇게 되다 보니, 로직 작성보다 메모리 관리에 더 많은 시간과 노력을 소모하는 경우가 많아 생산성이 떨어진다.
(물론 갓급 개발자의 경우에는, 직접 적절한 곳에 메모리를 할당/해제하는 작업을 통해 극강의 퍼포먼스를 발휘한다)

이에 따라, 위 코드는 문제 없이 컴파일은 되지만 메모리 누수가 발생한 코드가 된다.

반면, 가비지 컬렉터가 있는 JAVA의 경우에는 조금 다르다.
JVM에 내장된 가비지 컬렉터에 의해,
new로 선언된 변수를 별도로 해제하지 않아도 적절한 타이밍에 자동으로 처리된다.

GC의 핵심은 "Stop the World"이다.
쉽게 말하자면, GC 수행 시간 동안 GC 스레드를 제외한 모든 스레드를 일시정지 시킨다.
그리고 GC는 참조할 수 없는 객체에 대한 메모리를 해제한다.
GC가 끝난 뒤 일시정지 되었던 스레드의 작업들이 재개된다.

여기서 핵심은, '참조할 수 없는 객체'라는 점이다.
수많은 연구 결과에 의하면,
메모리는 "새롭게 할당된 영역일 수록 금방 해제될 확률이 높다"라는 규칙이 통하는데
그에 따라 JVM의 Heap 영역은 세 개의 영역으로 구분된다.
"
- Young 영역 : 새로 생성된 객체가 위치한다. 여기서 대부분의 객체가 unreachable(유효한 참조가 없는 객체 상태 : 가비지)한 상태에 빠지기 때문에, Young 영역에서의 GC에 의해 사라진다.
- Old 영역 : Young 영역에서 reachable(유효한 참조가 있는 객체 상태) 상태를 유지한 객체들이 Old 영역으로 복사된다. Young 영역보다 더 크게 할당되며, 상대적으로 Heap 크기가 더 크기 때문에 Young 영역보다 GC의 수행 빈도 수가 적다.
- Perm 영역 : Permanent Generation의 줄임말로, 객체의 생명주기가 영구적일 것으로 생각되는 객체가 위치한다. JAVA의 경우에는 Class 객체나 String 객체들이 해당한다.
"
여기서 reachable을 판단하는 작업을 Mark, Mark 내용에 따라 GC를 수행하는 작업은 Sweep이라 불러,
Mark & Sweep 컬렉터 원리 라고도 한다.

보다 자세한 내용은 갓 네이버(!)의 D2 블로그를 참조하자.
d2.naver.com/helloworld/1329

그러나 GC가 만능은 아니다.
오히려 몇몇 개발자들의 경우 Outo Garbage Collection이 프로그램의 성능을 떨어뜨린다고 주장한다.
그렇기에 서비스의 적용 환경에 맞춰 GC 적절히 튜닝하는 작업이 반드시 필요하다.
GC 튜닝은 시니어급 JAVA 개발자에게 있어 필수이자 핵심 역량이며,
이직 시 면접 질문에도 빈번하게 등장하는 키워드이다.

 

Go의 가비지 컬렉션

 

그렇다면, Go의 가비지 컬렉션은 어떻게 수행될까?

<가장 빨리 만나는 Go 언어 발췌>

GO 의 가비지 컬렉터는 JAVA 등의 그것과 구조적으로 다르다.
우리가 익히 쓰고 있는 JAVA를 포함하여,
Pyhon, Ruby, JavaScript 등의 언어들은 각 언어의 가상머신을 통해 가비지 컬렉션 기능을 사용하고 있다.

Go 의 경우에는 실행파일 안에 가비지 컬렉터가 내장된다.
그래서 Go는 프로그램이 매우 가벼우면서도, GC에 의해 생산성이 높은 특징을 가지게 된다.

그러나, GC의 기능적 측면에서는 약간의 차이가 있다.
JAVA 등의 VM에서 동작하는 GC는 위에서 언급한 Mark & Sweep 외에도
- 힙 내의 압축(compaction : GC 수행 시 살아남은 객체를 heap 끝으로 재배치 & heap 압축)
- 세대별 GC(Generational GC : 객체가 살아남은 횟수로 수명을 부여, 전체 스캔 빈도 감소로 효율 증가)
등의 기능이 있다.

Go의 GC는 이러한 기능은 수행하지 않고,
Concurrent Mark & Sweep(CMS)만을 수행하기에 다소 단순하고 가벼운 특징을 가진다.(Go 1.10 기준)
+. 현재 1.18버전 까지 등장하였으나, 여전히 비압축/비세대 원칙을 고수하고 있다.
"Garbage collection pauses should be significantly shorter, usually under 100 microseconds and often as low as 10 microseconds."라고 공식 버전 릴리즈 문서에 표현한 것으로 보아, 가벼움과 빠른 속도를 장점으로 계속 가지고 가려는 것으로 보인다. (blog.golang.org/go1.8)

보다 자세한 Go GC의 원리 및 JAVA GC와의 비교 분석은 갓 Line(!) 개발자분이 적으신 글을 참조하자.
engineering.linecorp.com/ko/blog/detail/342/

 

Go 언어의 GC에 대해 - LINE ENGINEERING

안녕하세요, LINE Ads Platform 개발을 담당하고 있는 Okada(@ocadaruma)입니다. 이번 포스팅에서는 개인적으로 쭉 관심있었던 Go 언어의 가비지 컬렉션(Garbage Collection, GC)에 대해 조사한 내용을 소개하겠��

engineering.linecorp.com

 

앞서 언급한 JAVA의 GC 튜닝 이슈와 마찬가지로,
Go 역시 GC 튜닝과 관련한 논의가 계속적으로 이루어지고 있다.

GO의 Gabage Collection은 편리한 기능이고 생산성을 높여주는 일임은 분명하지만,
우수한 서비스를 만들어내고 좋은 개발자가 되기 위해서는
Go의 GC 원리를 깊이 있게 이해하고 이를 튜닝하는 능력 또한 필요할 것임을 잊지 말자.

 

반응형

'Language > go lang.' 카테고리의 다른 글

[Go] Go언어 Channel  (0) 2020.09.14
[Go] 고루틴(go-routine)  (1) 2020.09.13
[Go] Go 언어 강 타입(strongly-typed)  (0) 2020.09.09
[Go]Go 언어 정적타입(static type)  (0) 2020.09.07
[Go] VSCODE에 Go 환경 설정  (1) 2020.09.05

댓글