JS 키워드 정리 - call by value / call by reference / 값이 메모리에 저장되는 방식 (수정)

Seuling·2022년 7월 7일
0

기술면접준비

목록 보기
2/4
post-thumbnail

키워드

값에 의한 전달 call by value

값에 의한 전달은 해당 값만 전달하고 메모리 주소는 전달하지 않는다. (다른 메모리 공간에 저장된 별개의 값이다.)

Call by value(값의 복사)는 말 그대로 복사된 값을 인자로 넘겨서 매개변수로 전달한다.

기본형(Primitive type)의 경우 call by value 방식으로 전달된다.

장점

복사하여 처리하기 때문에 안전하다. 원래의 값이 보존이 된다.

단점

복사를 하기 때문에 메모리가 사용량이 늘어난다.

얕은 복사 vs 깊은 복사

참조에 의한 전달 call by reference

두 개의 식별자가 하나의 참조를 위한 메모리 주소를 값으로 가지며 그 값이 참조하는 객체를 공유한다.

call by reference(주소값의 복사)는 실제 데이터가 존재하는 주소를 가리키는 주소값을 인자로 넘겨서 매개변수로 전달한다.

객체(ex. obj, array, function ...)는 call by reference 방식으로 전달된다.

장점

복사하지 않고 직접 참조를 하기에 빠르다.

단점

직접 참조를 하기에 원래 값이 영향을 받는다.(리스크)

자바스크립트에서는?

원시타입의 경우 call by value로 동작한다.
참조타입의 경우 call by reference로 동작한다.

원시타입

  • string, number, bigint, boolean, undefined, symbol
  • 변수에 할당될 때, 메모리의 고정 크기로 원시 값을 저장하고 해당 저장된 값을 변수가 직접적으로 가리키는 형태를 띈다.
  • 또한 값이 절때 변하지않는 불변성을 갖고있기때문에 때문에 재할당 시 기존 값이 변하는것 처럼 보일지 몰라도 사실 새로운 메모리에 재할당한 값이 저장되고 변수가 가리키는 메모리가 달라졌을 뿐이다.

참조타입

  • 원시타입을 제외한 나머지는 참조 타입이다
  • 배열, 객체, 함수가 대표적이며, 원시타입과 가장 큰 차이점은 변수의 크기가 동적으로 변한다는 것이다.
  • Object의 데이터 자체는 별도의 메모리 공간(heap)에 저장되며, 변수에 할당 시 데이터에 대한 주소 ( 힙(Heap) 메모리의 주소값)가 저장되기 때문에 자바스크립트 엔진이 변수가 가지고 있는 메모리 주소를 이용해서 변수의 값에 접근하게 되는것이다.

결국, 변수가 가리키는 메모리 공간에 저장되어 있는 값을 복사하여 전달한다는 관점에서 바라볼 때 자바스크립트는 항상 값에 의한 전달(Call By Value)만 존재한다고 말할 수 있다.

값이 메모리에 저장되는 방식

메모리 생존주기

  • 필요할 때 할당
  • 할당된 메모리를 사용 (읽기, 쓰기)
  • 더 이상 필요하지 않으면 해제 (자바스크립트 -> 암묵적 작동)

자바스크립트에서 메모리 할당

값 초기화

자바스크립트는 값을 선언할 때 자동으로 메모리를 할당한다.

함수 호출을 통한 할당

함수 호출의 결과 메모리 할당이 일어나기도 한다.

var d = new Date(); // Date 개체를 위해 메모리를 할당
var e = document.createElement('div'); // DOM 엘리먼트를 위해 메모리를 할당

메소드가 새로운 값이나 오브젝트를 할당

값 사용

값 사용이란 기본적으로는 할당된 메모리를 읽고 쓰는 것을 의미한다.
변수나 객체 속성의 값을 읽고 쓰거나 함수 호출 시 함수에 인수를 전달하여 수행 할 수 있다.

할당된 메모리가 더 이상 필요없을 때 해제하기

문제발생 -> 와이? -? "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 어렵기 때문에!

자바스크립트와 같은 고수준 언어들은 "가비지 콜렉션(GC)"이라는 자동 메모리 관리 방법을 사용한다.
가비지 콜렉터의 목적은 메모리 할당을 추적하고 할당된 메모리 블록이 더 이상 필요하지 않게 되었는지를 판단하여 회수하는 것이다.
이러한 자동 메모리 관리 프로세스가 궁극의 방법은 아니다. 왜냐하면 어떤 메모리가 여전히 필요한지 아닌지를 판단하는 것은 비결정적 문제이기 때문이다.

가비지 콜렉션

위에서 언급한 것처럼 "더 이상 필요하지 않은" 모든 메모리를 찾는건 비결정적 문제이다.
제한적 해결책 -> 주요한 가비지 컬렉션 알고리즘과 그 한계

참조

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다.
A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고한다.
예를 들어 모든 자바스크립트 오브젝트는 prototype 을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.

이 컨텍스트에서 "오브젝트"의 개념은 일반 JavaScript 오브젝트와 함수 범위(또는 정적 어휘 범위)를 포함하여 더 넓게 확장된다.

참조-세기(Reference-counting) 가비지 콜렉션

참조-세기 알고리즘은 가장 소박한 알고리즘이다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의한다.
이 오브젝트를 "가비지"라 부르며, 이를 참조하는 다른 오브젝트가 하나도 없는 경우, 수집이 가능하다.

예제
var x = {
  a: {
    b: 2
  }
};
// 2개의 오브젝트가 생성되었습니다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조됩니다.
// 나머지 하나는 'x' 변수에 할당되었습니다.
// 명백하게 가비지 콜렉션 수행될 메모리는 하나도 없습니다.


var y = x;      // 'y' 변수는 위의 오브젝트를 참조하는 두 번째 변수입니다.

x = 1;          // 이제 'y' 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었습니다.

var z = y.a;    // 위의 오브젝트의 'a' 속성을 참조했습니다.
                // 이제 'y.a'는 두 개의 참조를 가집니다.
                // 'y'가 속성으로 참조하고 'z'라는 변수가 참조합니다.

y = "mozilla";  // 이제 맨 처음 'y' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없습니다.
                // (역자: 참조하는 유일한 변수였던 y에 다른 값을 대입했습니다)
                // 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까요?
                // 아닙니다. 오브젝트의 'a' 속성이 여전히 'z' 변수에 의해 참조되므로
                // 메모리를 해제할 수 없습니다.

z = null;       // 'z' 변수에 다른 값을 할당했습니다.
                // 이제 맨 처음 'x' 변수가 참조했던 오브젝트를 참조하는
                // 다른 변수는 없으므로 가비지 콜렉션이 수행됩니다.

한계: 순환 참조

순환 참조를 다루는 일에는 한계가 있다. 다음 예제에서는 두 객체가 서로 참조하는 속성으로 생성되어 순환 구조를 생성한다.
함수 호출이 완료되면 이 두 객체는 스코프를 벗어나게 될 것이며, 그 시점에서 두 객체는 불필요해지므로 할당된 메모리는 회수되어야 한다.
그러나 두 객체가 서로를 참조하고 있으므로, 참조-세기 알고리즘은 둘 다 가비지 컬렉션의 대상으로 표시하지 않는다.
이러한 순환 참조는 메모리 누수의 흔한 원인이다.

function f() {
  var x = {};
  var y = {};
  x.a = y;         // x는 y를 참조합니다.
  y.a = x;         // y는 x를 참조합니다.

  return "azerty";
}

f();

표시하고-쓸기(Mark-and-sweep) 알고리즘

이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.
이 알고리즘은 roots 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미함).
주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 닿을 수 있는 오브젝트라고 표시한다.
그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.

이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다.
왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명하였다.

2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다.
지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다.
개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.

순환 참조는 이제 문제가 되지 않는다.
첫 번째 예제에서 함수가 반환되고 나서 두 오브젝트는 닿을 수 없다. 따라서 가비지 콜렉션이 일어난다.

두 번째 예제에서도 마찬가지입니다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없으면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어난다.

한계: 수동 메모리 해제.

어떤 메모리를 언제 해제할지에 대해 수동으로 결정하는 것이 편리할 때가 있다. 그리고 수동으로 객체의 메모리를 해제하려면, 객체 메모리에 도달할 수 없도록 명시하는 기능이 있어야 한다.

Reference

[Java Script] 원시타입과 참조타입

JS-📚-Call-by-Value-Call-by-Reference
자바스크립트의 메모리 관리

https://engineering.huiseoul.com/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EA%B0%80-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B4%80%EB%A6%AC-4%EA%B0%80%EC%A7%80-%ED%9D%94%ED%95%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-%EB%8C%80%EC%B2%98%EB%B2%95-5b0d217d788d

*** 면접 질문 답변 정리해야함!! ****
*** 가비지컬렉터 부분 정리해야함!! ****

profile
프론트엔드 개발자 항상 뭘 하고있는 슬링

0개의 댓글