본문 바로가기
프론트엔드/[언어] 자바스크립트

자바스크립트의 객체참조모델과 가비지콜렉션

by 도툐리 2024. 2. 27.

1. 자바스크립트의 객체 참조 모델

자바스크립트에서 객체는 참조 타입입니다.

이는 변수에 객체를 할당할 때, 실제 객체 자체가 아니라 객체의 메모리 주소(참조)가 저장된다는 것을 의미합니다.

 

(1) 객체 참조 모델의 주요 특징:

  1. 참조에 의한 할당과 전달:
    자바스크립트에서 객체를 변수에 할당하거나 함수에 인자로 전달할 때, 실제로 전달되는 것은 객체의 참조(메모리 주소)입니다. 이는 두 변수가 동일한 객체에 대한 참조를 공유할 수 있음을 의미합니다.
  2. 공유된 참조:
    두 변수가 같은 객체를 참조하고 있을 때, 한 변수를 통해 객체의 속성을 변경하면 다른 변수를 통해 객체를 조회했을 때도 이 변경사항이 반영됩니다. 이는 두 변수가 같은 메모리 위치를 가리키고 있기 때문입니다.
  3. 변경 불가능성과 참조:
    기본 데이터 타입(숫자, 문자열, 불리언 등)은 변경 불가능한 값(immutable values)으로 처리되며 값에 의한 복사가 일어납니다. 반면, 객체와 같은 참조 타입은 메모리 내의 객체를 가리키는 참조를 통해 다뤄지므로, 객체 자체는 변경 가능(mutable)합니다.

(2) 객체 참조 모델의 장단점:

장점:

  1. 메모리 효율성:
    객체를 참조로 처리함으로써, 큰 데이터 구조를 여러 곳에서 사용할 때 메모리를 절약할 수 있습니다. 같은 객체에 대한 여러 참조는 추가적인 메모리 공간을 거의 사용하지 않기 때문입니다.
  2. 성능 향상: 객체를 직접 복사하여 전달하는 것보다 참조를 통해 전달하는 것이 빠릅니다. 특히 대규모 객체의 경우, 참조를 통한 전달이 데이터 처리 시간을 상당히 단축시킬 수 있습니다.
  3. 상호작용의 용이성: 여러 변수가 같은 객체를 참조할 수 있으므로, 한 곳에서 객체를 변경하면 그 변경사항이 참조를 통해 해당 객체를 사용하는 모든 곳에 자동으로 반영됩니다. 이는 상태 관리가 중요한 애플리케이션에서 특히 유용할 수 있습니다.

단점:

  1. 가변성(Mutability)과 그로 인한 부작용(Side-effects):
    객체가 변경 가능하다는 것은 코드의 다른 부분에서 예상치 못한 변경이 발생할 수 있다는 것을 의미합니다. 이로 인해 버그가 발생하기 쉬워지고, 특히 크고 복잡한 애플리케이션에서 이러한 문제를 진단하기 어려울 수 있습니다.
  2. 참조 관리:
    객체 참조를 사용할 때, 참조가 더 이상 필요하지 않을 때 메모리에서 해당 객체를 적절히 해제하는 것이 중요합니다. 자바스크립트는 가비지 컬렉션을 통해 이를 자동으로 처리해줍니다.

자바스크립트에서 객체 참조 모델을 효과적으로 사용하기 위해서는 이러한 장단점을 잘 이해하고, 객체의 변경 가능성과 참조의 공유 방식에 주의를 기울여야 합니다. 객체를 불변(immutable)으로 만들거나, 상태 변경을 엄격하게 관리하는 방법 등을 통해 부작용을 최소화할 수 있습니다.

 

 

 

2. 자바스크립트의 가비지 콜렉션

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

 

원시값, 객체, 함수 등 우리가 만드는 모든 것은 메모리를 차지합니다. 그렇다면 더는 쓸모 없어지게 된 것들은 어떻게 처리될까요?

지금부턴 자바스크립트 엔진이 어떻게 필요 없는 것을 찾아내 삭제하는지 알아보겠습니다.

 

(1) 가비지 콜렉션 기준

자바스크립트는 도달 가능성(reachability) 이라는 개념을 사용해 메모리 관리를 수행합니다.

‘도달 가능한(reachable)’ 값은 쉽게 말해 어떻게든 접근하거나 사용할 수 있는 값을 의미합니다.

도달 가능한 값은 메모리에서 삭제되지 않습니다.

  1. 아래 소개해 드릴값들은 그 태생부터 도달 가능하기 때문에, 명백한 이유 없이는 삭제되지 않습니다.
    • 현재 함수의 지역 변수와 매개변수
    • 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 매개변수
    • 전역 변수
    • 기타 등등
    -> 이런 값은 루트(root) 라고 부릅니다.
  2. 루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 됩니다.

자바스크립트 엔진 내에선 가비지 컬렉터(garbage collector)가 끊임없이 동작합니다.

가비지 컬렉터는 모든 객체를 모니터링하고, 도달할 수 없는 객체는 삭제합니다.

 

<도달할 수 없는 섬>

객체들이 연결되어 섬 같은 구조를 만드는데, 이 섬에 도달할 방법이 없는 경우, 섬을 구성하는 객체 전부가 메모리에서 삭제됩니다.

따라서 순환 참조가 있는 객체들도, 그들이 외부에서 도달할 수 없는 상태가 되면 메모리에서 회수될 수 있습니다.

 

(2) 가비지 콜렉션 내부 알고리즘

<mark-and-sweep>

'mark-and-sweep’이라 불리는 가비지 컬렉션 기본 알고리즘에 대해 알아봅시다.

 

가비지 컬렉션은 대개 다음 단계를 거쳐 수행됩니다.

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

루트에서 페인트를 들이붓는다고 상상하면 이 과정을 이해하기 쉽습니다. 루트를 시작으로 참조를 따라가면서 도달가능한 객체 모두에 페인트가 칠해진다고 생각하면 됩니다. 이때 페인트가 묻지 않은 객체는 메모리에서 삭제됩니다.

 

이는 기존 reference count 알고리즘에 의한 가비지 콜렉션의 단점을 보완한, 보다 효율적인 방식입니다.

왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트"이지만 역은 성립하지 않기 때문이죠.

( 그 역에 대한 반례가 바로 "도달할 수 없는 섬")

 

 

자바스크립트 엔진은 가비지 컬렉션을 더 빠르게 하는 다양한 최적화 기법을 적용합니다.

<mark-and-sweep 최적화 기법>

  • generational collection(세대별 수집)
    객체를 '새로운 객체’와 '오래된 객체’로 나눕니다. 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이런 객체를 '새로운 객체’로 구분합니다. 가비지 컬렉터는 이런 새로운 객체를 공격적으로 메모리에서 제거합니다. 일정 시간 이상 동안 살아남은 객체는 '오래된 객체’로 분류하고, 가비지 컬렉터가 덜 감시합니다.
  • incremental collection(점진적 수집)
    방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 mark 하는데 상당한 시간이 소모됩니다. 가비지 컬렉션에 많은 리소스가 사용되어 실행 속도도 눈에 띄게 느려지겠죠. 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행합니다. 작업을 분리하고, 변경 사항을 추적하는 데 추가 작업이 필요하긴 하지만, 긴 지연을 짧은 지연 여러 개로 분산시킬 수 있다는 장점이 있습니다.
  • idle-time collection(유휴 시간 수집)
    가비지 컬렉터는 실행에 주는 영향을 최소화하기 위해 CPU가 유휴(=available) 상태일 때에만 가비지 컬렉션을 실행합니다.

 

(3) 요약

  • 가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없습니다.
  • 객체는 도달 가능한 상태일 때 메모리에 남습니다.
  • 참조된다고 해서 도달 가능한 것은 아닙니다. 서로 연결된 객체들도 도달 불가능할 수 있습니다.

 


참고자료

https://ko.javascript.info/garbage-collection

 

가비지 컬렉션

 

ko.javascript.info

 

 

댓글