primitive type VS reference type

GI JUNG·2022년 12월 30일
2

typescript

목록 보기
5/10
post-thumbnail

원시타입과 참조타입에 대해서 이해했다고 생각했지만, 코어 자바스크립트와 딥 다이브 책을 보면서 많은 내용을 놓쳤구나라는 생각이 들어 이에 대해서 정리하고자 한다.
그저 참조 타입은 변수가 값이 아닌 주소를 가리키기 때문에 const arr = [1, 2, 3]; const copyArr = arr를 하고 copyArr에 값을 추가하면 복사당한 arr도 변화가 있다는 것 정도만 간략하게 알고 넘어갔었다.
이를 공부하게 하면서 위의 두 개의 책을 기반으로 나머지는 여기저기의 사이트에서 지식을 얻어 정리했다.

🍀 Computer Memory

javascript는 어떻게 메모리를 관리할까 이전에 컴퓨터에서의 일반적인 의미의 메모리란 무엇이고 어떻게 동작하는지 간략하게라도 짚고 넘어갈 필요가 있어 정리해본다.

What is Memory?

디지털 메모리 시스템은 플립플롭(flip flop)으로 이루어져 있다고 한다. 이 플립플롭들이 모여 필요할 때 디지털 상태(0 or 1)를 생성하고 프로그래밍에 의해 변경될 때까지 무한정 디지털 상태를 유지할 수 있다. 결국 컴퓨터 메모리란 여러 개의 비트(플립플롭)로 구성된 커다란 상자와 같이 생각할 수 있을 것 같다.

💡 플립플롭(flip flop)이란????
각각의 플립플롭은 몇 개의 트랜지스터를 갖고 있으며 하나의 비트를 저장할 수 있다. 또한 각각의 플립플롭은 고유한 식별자를 통해 해당 플립플롭의 위치를 알 수 있으며 여기에 읽거나 쓰는 것이 가능하다.

<플립플롭의 예시>


🤔 공부하면서 든 생각인데.... 뒤에서 참조객체 내용과 flip flop에서의 고유 식별자 (unique identifier)와 연관지어 생각이 났다. 참조 객체는 객체의 식별자가 메모리 주소를 참조하는데 이 때 참조하는 메모리 주소가 flip flop의 고유 식별자와 같지는 않아도 1-1매칭을 이루지 않을까 생각해본다. 즉 한 마디로 표현해보자면, 변수명(식별자) -> 메모리 주소 = flip flop의 고유식별자같다. 왜냐하면 컴퓨터는 0과 1을 쓰거나 읽기 위해 비트가 저장된 flip-flop에 접근해야 된다는 소리인데 이 때 접근할 수 있는 방법은 flip-flop의 고유 식별자말고 없지 않나?라는 생각에서 출발하게 됐다. 아시는 분 있으면 알려주세요 ㅜㅜ 😭😭😭

그리고 아래는 bit의 흔적을 보여줄 수 있는 예시라 넣어본다.
어렸을 때 친구와 게임을 공유하기 위해서 게임CD를 사용해본 사람은 알 것이다. 알록달록한 부분에 스크래치가 나면 CD를 다시 쓸 수 없었던 것을.... 이제와서 보니 CD 뒷판 알록달록한 부분에 데이터(bit의 흔적)가 담겨 있었던 것이었다.

< CD에 encoding된 정보 >

bit & byte & word

컴퓨터는 인간과 달리 오직 0과 1만을 읽고 쓸 수 있다. 따라서 컴퓨터 메모리의 영역, 공간을 측정하는 단위가 필요해서 bit, byte, word의 개념이 나온 것 같다. 만약 단위를 정하지 않았다면 1TB를 오직 bit로만 소통한다면 꽤나 골치아플 것이다.


< bit & byte & word >

bit

bit는 0과 1 나타낼 수 있으며 컴퓨터 메모리의 가장 기본적인 단위이다. 다르게 표현하자면 bit는 스위치와 같아 on(켜짐) 또는 off(꺼짐)의 두 값 중 하나를 나타낼 수 있다.

그리고 어디서 주워들은 얘기인데 어떤 과학자가 세상을 오직 0과 1로만 표현할 수 있다고 하여 그 과학자가 시발점이지 않나 싶다.

byte

byte는 메모리에 저장되는 실질적인 단위로서 8bit가 모여 1byte를 구성한다. 추가적인 설명을 하자면 C언어에서 정수 short형은 2byte이고 char형은 1byte이다. 여기서 흔히 2byte16bit라고 말하지 않는 것을 생각해보면 여기서 실질적인 단위가 byte임을 지금이라도 짐작할 수 있다.

아래는 byte단위로 표현된 메모리의 일부분을 보여준다.

word

word는 위의 그림에서와 같이 4byte가 1word를 구성한다.

Number

숫자가 메모리에 어떻게 저장되는지 알아보자. 숫자형에는 정수형 실수형 등등이 있지만, 여기서는 정수형만 다루고 넘어갈 생각이다. 정수는 일반적으로 4byte 또는 1word(32bit)를 사용하여 메모리에 저장된다. 따라서, 최대 2^32 - 1 = 4,294,967,295까지의 정수를 저장할 수 있다.

아래는 1, 2, 3, 4, 5를 표현한 그림이다.

이진법 계산

이에 앞서 이진법을 계산하는 방법을 배워보자. 숫자 13을 이진법을 표현하자면 1101=23+22+210+120=8+4+0+1=131101 = 2^3 + 2^2 + 2^1*0 + 1*2^0 = 8 + 4 + 0 + 1 = 13과 같이 된다.

Text

텍스트는 정수로 변환 된 다음 메모리에 저장된다.
여기서 텍스트를 정수로 변환하는 것을 encoding(인코딩)이라고 한다. 인코딩 방식에는 여러가지가 있는데 여기서 사용된 인코딩 방식은 우리가 많이 들었던 ASCII(American Standard Code for Information Interchange)를 이용한 것이다. 하지만, ASCII의 abbreviation(약어)에서 알 수 있듯이 ASCII는 미국에서 제정된 즉, 영어를 기반으로 만들어진 표준 문자 인코딩으로서 한국어를 표기할 수 없었다. 한국어 뿐만 아니라, 중국어, 일본어(CJK, Chinese-Japanese-Korean)에서 사용하는 문자 집합은 조합 개수가 많아 CJK 문자를 처리하기 위한 별도의 방안이 필요했다고 한다. 현재 한국어 인코딩은 EUC-KR과 UTF-8방식이 존재한다고 한다.

한글 인코딩과 인코딩에 대한 역사를 알고 싶다면 naver tech blog

텍스트에 대한 서론은 여기까지 마치고 우리가 코딩을 하면서 한 번쯤은 봐봤을 ASCII 코드를 한 번 직접 살펴보자

console.log('a'.charCodeAt()) // 97
console.log('b'.charCodeAt()) // 98
console.log('c'.charCodeAt()) // 99

코드에서 a, b, c에 대한 ASCII 코드는 97, 98, 99임을 확인할 수 있다.

🍀 javascript memory lifecycle

mdn memory lifecycle에서 살표볼 수 있듯이 javascript는 C와 같은 정적 타입 언어와 달리 자동으로 메모리를 할당하고 가비지 컬렉터(garbage collector)에 의해 자동으로 수거된다.

javascript는 객체를 만들 때 JS Engine은 메모리를 할당하고 메모리가 필요없으면 해제한다.

메모리 할당: 변수를 선언할 때 필요한 만큼의 메모리 공간을 사용하기 위해 예약하는 과정
메모리 해제: 더 이상 필요없어신 메모리 공간을 다른 용도로 사용할 수 있게 준비하는 과정

자바스크립트의 메모리 생명주기; memory life cycle은 아래와 같이 3단계로 이루어진다.

Memory Allocation(메모리 할당) -> Using Memory(메모리 사용) -> Releasing Memory(메모리 해제)

1. 메모리 할당(Memory Allocation)

우리가 필요한 개체의 공간만큼 필요한 메모리 공간을 차지하는 과정이다.

Memory Allocation Example

// string을 저장하기 위한 메모리 할당
const str = 'something' 

// 객체를 저장하기 위한 메모리 할당
const person = { 
	name: 'jjung',
  	age: 29
}

// 함수를 저장하기 위한 메모할당
function printNumbers(...nums){
	nums.forEach(num => console.log(num))
}

2. 메모리 사용(Using Memory)

메모리에 저장된 data를 읽거나 쓰는 것을 메모리 사용이라 한다. 즉, 프로그램에 의해 변수가 참조하는 값에 접근하여 읽거나 쓰는 것으로 이해하면 편할 것 같다.

Using Memory Example

let num = 100;

// num을 읽기 위한 메모리 사용
console.log(num) 
// num에 값을 쓰기 위한 메모리 사용
num = 200;

const person = { 
	name: 'jjung',
  	age: 28
}

// person object의 name을 읽기 위한 메모리 사용
console.log(person.name)
// person object의 age에 값을 쓰기 위한 메모리 사용
person.age = 29 // 올해 29살이 됐다... ㅜㅜ😭 내모레 삼십...;;

3. 메모리 해제(Releasing Memory)

더 이상 사용하지 않는 변수를 메모리에서 제거하는 과정이다. 이 때 제거하는 과정을 garbage collection이라고 하며 메모리를 해제하는 것은 garbage collector라고 한다.

🚗 garbage collector메모리 할당을 계속 모니터링하며 변수가 필요한지 아닌지를 결정 후 필요하지 않다고 생각하면 메모리를 해제한다. 그리고 garbage collection을 할 때 mark and sweep algorithm이 쓰인다고 한다.

Releasing Memory Example

function add(x){
  	let num = 10;
  
	return x + y;
}

// add함수가 실행되고 function scope를 벗어날 때 num 변수는 메모리에서 해제된다.
add(2)

메모리 해제에 관한 코드 예제는 찾지 못 했는데 함수 호출 시 num 변수 선언되지만, 함수 실행이 끝난 후에는 num 변수는 사라진다고 알고 있어 이에 적절한 예제 같아서 넣었다. 👉🏻 틀렸다면 댓글로 알려주시기 바랍니다!! ㅜㅜ

추가적으로 메모리 관리에 있어서 mark and sweep algorithm 또는 memory leaks(메모리 누수)에 관련해서는 여기에 정리해놓았다.

🍀 varible & identifier

본인은 변수와 식별자를 지금까지 혼용해서 쓰고 있었다. 변수 = 식별자라고 알고 있었다. 하지만, 변수와 식별자는 같으면서도 다른 개념이다. 아래를 살펴보자

variable(변수): 변할 수 있는 값으로 데이터를 의미한다.

indentifier(식별자): 데이터(변수)를 식별하는 이름. 즉, 변수명을 뜻한다

변수와 식별자를 소개하는 이유는 메모리를 참조하는 데 있어서 변수가 아닌 식별자임을 명확하게 하기 위해서이다.

🍀 Primitive Type & Reference Type

🍀 Stack memory & Heap memory

메모리 영역이 완전 별개 공간에 따로따로 위치하는 줄 알았는데 위에서 메모리를 설명한 것이 결국 하나의 메모리에서 영역을 구분지어 사용하는 것을 설명.
또한 stack과 heap이 겹쳐 overflow가 일어날 수 있음을 제기.
정적 할당과 동적할당.
동적 할당을 위해서는 heap memory를 사용해야함을 언급.
여기에서 chrome developer tool -> memory에서 주소가 일치함을 증명

📚 참고

how does value being stored in memory
pass by value VS pass by reference
core javascript
havard cs50
how does memory being managed in javascript
memory structure
memory structure1
memory structure2
memory structure3
how does data stroed in v8 javascript engine
see heap and stack memory by debugging -> 이거 도전해보기!!

profile
step by step

0개의 댓글