가비지 컬렉션(Garbage Collection)에 대하여

gyojinnK·2024년 3월 17일
post-thumbnail

가비지 컬렉션은 학과생 때 자주 듣던 단어이다.

이번엔 가비지 컬렉션을 씹고 뜯고 맛보면서 확실하게 내 것으로 만들어보자.

가비지 컬렉션(Garbage Collection, GC)

프로그래밍에서 메모리 관리는 매우 중요한데 시스템의 성능과 직관되기도 하기 때문이다.
먼저 단어에 대한 이해부터 들어가보자.

단어 뜯어보기

프로그래밍적 의미는 내가 이해하기 위해서 의미를 부여해보겠다.

  • Garbage
    • 사전적 의미: 쓰레기, 찌꺼기, 보잘것없는 것
    • 프로그래밍적 의미: 쓸모없는, 유효하지 않은 메모리
  • Collection
    • 사전적 의미: 수집, 회수
    • 프로그래밍적 의미: 수집, 회수

즉, 가비지 컬렉션이란 프로그램의 사용하지 않는, 쓸모없는 메모리를 수집, 회수하는 역할.
가비지 컬렉션 == 쓰레기 수집가!

가비지 컬렉션은 쓰레기 메모리를 수집해주는 아주 고마운 녀석!
그렇다면 가비지 컬렉션은 어디에서 어떻게 쓰일까?


가비지 컬렉션(GC)

나무위키에 보면 가비지 컬렉션은 메모리 관리 방법 중 하나로, 프로그래머가 동적으로 할당한 메모리 영역 중 더 이상 쓰이지 않는 영역을 자동으로 찾아내어 해제하는 기능이라고 서술돼있다.

또한, 과거의 프로그래밍 언어(BASIC, FORTRAN, C ...)에서는 개발자가 직접 메모리를 할당하고 Garbage를 찾아 수동으로 해제했다고 한다.
C를 사용해봐서 이부분은 쉽게 공감이 되었다.
C에서는 malloc 함수를 사용해서 동적으로 메모리를 할당하고 free 함수로 해제했었다. 진짜 옛날이라 추억에 잠기네...

하지만 이는 사람이 하는 일이고 그렇기에 항상 완벽할 수는 없는 노릇이다.
개발자의 실수로 해제했던 메모리를 다시 사용하거나, 해제한 메모리를 또 해제한다거나 하는 불상사가 발생하기도 했다고 한다.
그 이후는... 에러와 크래시의 축제...

이렇게 메모리 관리에서 실수로 인해 발생하는 많은 문제점들을 해결하고자 제시된 것이 GC, Garbage Collection인 것 이다.

GC는 쓰레기 수집기이다.
이 쓰레기 수집기는 할당과 해제를 제공한다.


가비지 컬렉션(GC)를 채택한 언어

GC를 채택하거나 발전 과정에서 추가된 언어들을 보자

  • ALGOL, BASIC, D, Erlang, Go, Haskell, JavaScript, Juila, Lua, OCaml, Perl, PHP, Python, Ruby, Smalltalk, Swift, C#, F#, Visual Basic, Java, Kotlin ...
    너무 많다. 조금 생략한 언어들도 있을 정도로 많다.

여기서 내가 자주 사용하는 JavaScript의 GC를 조금 뜯어보자.


JavaScript의 GC

자바스크립트는 눈에 보이지 않는 곳에서 메모리 관리를 수행한다.

자바스크립트에서의 GC의 기준에 대해 알아보자.

  • 도달 가능성(Reachability)

    • 어떻게든 접근하거나 사용할 수 있는.
      => 도달 가능한 값은 어떻게든 접근하거나 사용할 수 있는 값!
      이러한 도달 가능한 값은 메모리에서 삭제되지 않는다.
  • 명백한 이유 없이 메모리에서 삭제되지 않는 값들

    • 현재 함수의 지역 변수와 매개변수
    • 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
    • 전역 변수
    • ...
      이러한 값들은 root라고 불린다.
      root가 참조하는 값이나 서로 연관되어 루트에 참조할 수 있는 값은 도달 가능한 값이 된다.
// user엔 객체 참조 값이 저장
let user = {
  name: "John"
};

전역 변수 user{name: "John"} 이라는 객체를 참조한다.
이 상태에서 만약

user = null;

null 값으로 덮어쓰게 된다면 user는 {name: "John"}을 참조하던 것을 끊어버리고
null을 참조하며 연결된다.
이렇게 된다면 {name: "John"}은 user를 통해서 도달할 수 없다. 물론 다른 방법도 존재하지 않는다.
이때 {name: "John"}은 도달할 수 없는 값이 되어버리는 것이다.
그렇다면? 위에서 설명한 것처럼!
자바스크립트 GC의 기준(도달 가능한 값은 명백한 이유 없이 메모리에서 삭제되지 않는다.)에서 벗어나면서 삭제 대상이 되는 것이다.

자바스크립트 엔진 속 GC는 도달 가능성이 없는(접근할 수 없는, 연결되어 있지 않은) 데이터를 Garbage로 간주하고 메모리를 해제한다.
즉, 배편이 끊긴 "섬"을 기억하지 않는 것이지!


GC의 내부 알고리즘

이번엔 조금 어려운 내용이다.
참고한 아티클의 내용을 정리하는 식으로 풀어봐야겠다.

mark-and-sweep

mark-and-sweepGC의 기본 알고리즘이다.
GC는 대게 다음 단계를 거쳐 수행된다.

  • GC는 root 정보를 수집하고 이를 mark(기억) 한다.
  • root가 참조하고 있는 모든 객체를 방문하고 이것들을 mark 한다.
  • mark 된 모든 객체에 방문하고 그 객체들이 참조하는 객체도 mark 한다.
  • 한번 방문한 객체는 전부 mark하기 때문에 같은 객체를 다시 방문하는 일은 없다.
  • root에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복한다.
  • mark 되지 않은 모든 객체를 메모리에서 삭제한다.

이미지를 통한 이해를 원한다면 내가 참고했던 글을 확인해보면 된다. 이해하기 쉽게 잘 풀어서 설명되어 있다. 나는 텍스트로 ㅎㅎ..

나의 이해를 돕기 위해서 전역 범위라는 파워 버튼(스위치)가 있고 그 버튼에 연결된 많은 전구들이 있고 모든 전구는 방금 구매했기 때문에 하자가 없다고 가정해보자.
만약 내가 전역 범위라는 버튼을 누른면 연결되어 있는 모든 전구들에 불이 들어올 것이다.
이때 불이 들어오지 않는 전구들이 있다면, 그 전구는 파워 버튼과 연결되지 않았다는 말이다.
그럼 이 전역 범위와 연결되지 않은 전구들(도달 가능성이 없는)은 GC의 해제 대상이 되는 것이다!
아 비유 너무 좋은데 ㅎㅎ

내부 알고리즘은 위와 같이 동작한다.

그런데 자바스크립트에서는 이 알고리즘을 최적화해서 사용한다고 한다.
과연 자바스크립트는 어떤 최적화를 통해서 GC을 이용할까?


Javascrip 엔진의 GC 최적화 기법

자바스크립트 엔진은 실행에 영향을 미치치 않으면서 GC를 더 빠르게 하는 최적화 기법을 적용한다고 한다.

  • generational collection(세대별 수집)
    • 객체를 새로운 객체오래된 객체로 나눈다.
    • 객체가 생성 이후 제역할을 빠르게 수행하고 쓸모가 없어진다면, 이런 객체를 새로운 객체로 구분하고 공격적으로 메모리에서 제거한다.
    • 그렇지 않은, 일정 시간 이상 동안 살아남은 객체는 오래된 객체로 분류하며 GC는 이를 덜 감시한다.
  • increamental collection(점진적 수집)
    • 모든 객체를 꼭 한 번은 mark하기 때문에 객체가 많다면 엄청난 시간이 소요된다.
    • 이를 해결하기 위해 GC를 여러 부분으로 분리하고 각 부분을 별도로 수행한다.
    • 긴 지연짧은 지연 여러 개로 분산시킬 수 있다.
  • idle-time collection(유휴 시간 수집)
    • GC는 실행에 주는 영향을 최소화하기 위해서 CPU가 유휴 상태일 때만 GC를 실행한다.

위에서 설명한 최적화 방법 말고도 많은 최적화 방법이 있다.
각 언어마다 채택하고 있는 방법이 다르다.

큰 의미를 두지 않았던 GC에 대해 알아보는 시간을 가졌다.
생각했던 것보다 딥하게 해보았는데 머리에 쥐가 나는 기분이다.


refer

profile
기록하고 꺼내보고

0개의 댓글