면접 단골 질문인 Garbage Collection(이하 GC, 가비지 컬렉션)에 대해 알아본다.
이 글에서는 글 제목처럼,
개념 / 유래 / 한계 / GC에 대해 알아야 하는 이유 등에 대해 알아본다.
개념
Garbage Collection,
메모리 관리 방법 중 하나로
말 그대로 쓰레기를 수집하는 기능이다.
여기서 "쓰레기"란 실제 쓰레기를 말하는 것이 아니라,
개발자가 동적으로 할당한 메모리 영역 중
더 이상 쓰이지 않는 영역을 말하며,
가비지 컬렉션은 그러한 영역을 자동으로 찾아내어 해제하는 기능이다.
보통 메모리에 접근이 불가능하면 "쓰레기"로 본다.
오래된 프로그래밍 언어 중 하나인 LISP의 아버지라 불리는
존 매카시가 1959년에 LISP의 메모리 관리를 위해
처음 만들었다고 알려져 있다.
유래
LISP와 같은 옛날의 프로그래밍 언어들은
BASIC처럼 동적 메모리 할당 기능이 아예 없거나,
FORTRAN이나 C처럼 프로그래머가 동적 메모리 할당부터
해제까지 모두 수동으로 해 줘야 하는 방식이었다.
하지만 이 세상에 완벽한 개발자는 없는 법.
메모리를 할당해놓고 해제해야 하는데 까먹어서 메모리 누수가 생기거나,
해제한 메모리를 실수로 다시 사용하거나,
해제했던 메모리를 또다시 해제한다거나 등
여러 실수가 일어날 여지가 충분히 있고
실제로 자주 일어났다고 한다.
더욱이, 일반적인 버그는 재현이 가능하고
버그가 있는 부분 근처에서 오류가 발생되어야
디버깅하기 쉬운데,
메모리 관련 버그는 한~참 떨어진 곳에서 발생되고
재현이 불가능한 경우도 많아서
개발자에게는 지옥의 디버깅을 선사해준다고 한다.
이러한 문제들을 해결하기 위해 가비지 컬렉션가 탄생되었고,
이러한 GC 기능을 채택한 프로그래밍 언어의 경우는
개발자가 직접 메모리 할당 및 해제를 할 필요가 없다.
GC가 프로그램이 실행되는 중간중간에
쓸모가 없어진 메모리(즉, 쓰레기)를 알아서 수집한다.
한계 & GC에 대해 알아야 하는 이유
이 글 이후에 여러 GC 동작 방법에 대해 작성하겠지만,
어떤 방식의 GC를 사용하든
프로그램 실행 시간에 작업을 하는 이상
성능 하락을 피할 수가 없다.
이뉴는, GC를 실행하기 위해
JVM이 애플리케이션 실행을 멈추기 때문이다.
그래서 GC에는 'stop-the-world'라는 용어가 따라다닌다.
단어 그대로,
GC를 실행하려면 world(애플리케이션)을 stop 해야 한다는 뜻이다.
이러한 'stop-the-world'가 발생하면
GC를 실행하는 쓰레드를 제외한
나머지 쓰레드는 모두 작업을 멈추고,
GC 작업을 완료한 이후에야 다시 작업을 시작한다.
어떤 GC 알고리즘을 사용하더라도
'stop-the-world'가 발생하기 때문에
성능 하락을 피할 수 없다는 것이다.
또한 GC만 철썩같이 믿어도 안된다.
GC가 존재하더라도 메모리 누수는 여전히 발생 가능하다.
GC는 더 이상 접근이 불가능한 객체만 회수하기 때문이다.
프로그램 실행 중에 앞으로는 두 번 다시 사용하지 않을 객체가 있다고 가정하자.
하지만 개발자의 실수로
그 객체에 접근할 수 있는 경로가 하나라도 남게 되면,
GC는 해당 객체가 이후에 또다시 사용할 가능성이 있다고 판단하고
회수하지 않는다.
이런 객체가 하나가 아니라 여러 개여서
프로그램이 실행되며 점점 누적되어 간다면,
메모리가 펑 터져 죽고 말 것이다.
프로그램이 복잡해질수록
이러한 종류의 메모리 이슈가 발생할 확률이 높아진다.
즉, GC는 만능이 아니기 때문에,
GC의 동작 원리를 정확히 알아야
프로그램 개발 및 운영 시에 신중한 코드 작성이 가능해진다.
다음 글에서는
GC 동작 원리 및 방법 등에 대해 자세히 알아본다.
끝!