자바스크립트엔진(1) / 자바스크립트의 특징, 데이터 타입별 메모리 할당 - call stack & memory heap
1. 자바스크립트의 특징
우리가 작성한 코드들은 자바스크립트 엔진에 의해 한 줄 한 줄씩 번역되고 실행됩니다.
이러한 특징을 가진 프로그래밍 언어를 인터프리터 언어라고합니다.
이와 비교되는 언어는 컴파일 언어입니다. 컴파일 언어의 예로 JAVA가 있습니다.
JAVA의 경우 컴파일러를 통해 통째로 번역을 완료한 이후(컴파일링), 실행파일을 거쳐 컴퓨터에서 실행하게 됩니다.
컴파일 언어의 경우 초기 번역에 시간이 걸리지만 일단 번역이 완료되었다면, 이후의 진행은 빠릅니다.
자바스크립트는 한 줄 번역 실행, 한 줄 번역 실행하기 때문에 초반 실행속도는 빠르지만 이후에는 상대적으로 컴파일 언어에 비해 느릴 수 있습니다.
이외에도 자바스크립트는 아래와 같은 특징들을 가지고 있습니다.
- 웹 페이지를 위한 프로그래밍 언어. (하지만 브라우저 외부 환경에서도 자바스크립트 엔진이 있다면 사용 가능하다 : node js)
- Single Thread : 한번에 하나의 일만 수행가능. 동기적 언어.
- Just-in-time compiled (interpreted) : 컴파일링 하지 않고 한 줄 한 줄 해석하고 실행
- First-class function : 일급함수를 가진 언어.
(함수를 다른 변수와 동일하게 다루는 언어는 일급 함수를 가졌다고 표현합니다. 예를 들어, 일급 함수를 가진 언어에서는 함수를 다른 함수에 인수로 제공하거나, 함수가 함수를 반환할 수 있으며, 변수에도 할당할 수 있습니다. - MDN)
- 프로토 타입을 베이스로한 멀티패러다임 언어 : 객체지향형, 명령형, 선언형(함수형 프로그래밍 등) 스타일을 지원한다.
- 동적인 언어 : 타입이 런타임에 결정되는 언어. 코드가 짜여질 때 자료형이 결정되는 것이 아니라 실행 시 결정된다. 매번 타입을 써줄 필요가 없기 때문에 빠르게 코드를 작성할 수 있지만, 실행 도중 변수에 예상치 못한 타입이 들어와 Type Error가 발생할 수 있다.
(타입스크립트 - 정적인 언어 : 코드를 작성할 때 타입을 결정되는 언어. 타입 에러로 인한 문제점을 초기에 발견할 수 있어 타입의 안정성이 올라간다.)
2. 자바스크립트의 동작원리
자바스크립트 언어를 해석하기 위해, 브라우저들은 각각의 자바스크립트 엔진을 가지고 있습니다.
브라우저 | 자바스크립트 엔진 |
Explore | Chakra |
Edge | V8 |
Chrome | V8 |
Safari | Javascript core |
Firefox | Spidermonkey |
Node js | V8 |
크롬, 엣지에서 사용하는 대표적인 자바스크립트 엔진 V8은 아래와 같이 Memory Heap과 Call Stack으로 구성됩니다.
1) Call Stack
- Primitive (원시) 데이터 (number, string, boolean, null, undefined, symbol) 가 저장됩니다.
- 실행 콘텍스트를 통해 변수 식별자(이름)가 저장되고, 스코프체인 및 this 관리, 코드 실행 순서 관리 등을 수행합니다.
2) Memory Heap
- 오브젝트(함수, 배열, 정규 표현식..)와 같은 참조 타입 데이터가 저장됩니다.
3. 데이터 타입별 메모리 할당 과정 : 원시 vs 객체
1) Primitive 원시 자료형 - Call Stack !
let apple = '🍎';
변수를 선언하면 데이터 값(🍎)이 메모리에 할당됩니다.
apple이라는 변수는 직접적으로 데이터 값을 가리키는게 아닌 주소값(Ox0016)을 가리키게 됩니다.
🍎의 주소값인 Ox0016 값이 apple 변수에 저장되는 것입니다.
let grape = '🍇'
또 다른 변수를 선언하면 마찬가지로 콜스택 데이터 값에 할당되고,
그 주소값인 Ox0015 값이 grape라는 변수에 저장됩니다.
Case 1
apple = '🍇' 로 데이터 값을 재할당하게 되면,
apple이라는 변수 안에 저장되어 있는 주소 Ox0016의 데이터 값인 🍎가 => 🍇로 변화되는 것이 아니라
apple안에 저장되어 있는 주소값이 Ox0015로 바뀌게 됩니다. (아래 그림 참조)
결과적으로 참조되지 않게 되는 Ox0016 - 🍎 값은. 자바스크립트 Garbage collection에 의해 이후 삭제되게 됩니다.
Case 2
현재 apple 이라는 변수는 Ox0015 - 🍇 값을 가리키고 있습니다.
apple = '🍌' 라는 기존에 없던 새로운 값으로 데이터 값을 다시 할당하면 어떻게 될까요?
Ox0015에 들어있는 🍇 값이 ==> 🍌로 변화되는 것이 아닌,
🍌가 새로운 데이터 값으로 할당되게 되고 그 주소값(Ox0014) 을 apple에 할당하게 됩니다. (아래그림)
let banana = '🍌' 라는 새로운 변수를 만들었다면,
🍌 데이터를 새롭게 메모리에 할당하는 것이 아닌,
기존에 🍌데이터 값이 있기 때문에 그것을 가리키는 Ox0014 주소값을 banana 변수에 저장합니다. (아래그림)
apple과, banana는 같은 값.
console.log(!!apple === banana) // true.
Case 3
현재 apple 변수에는 Ox0014 - 🍌 값이 들어 있습니다.
아래 처럼 바나나라는 변수에 apple을 할당하고, 바나나의 값을 바꾸게 되면, apple의 값도 🍎로 바뀌게 될까요?
let banana = apple;
banana = '🍎'
아닙니다. apple의 값에는 변화가 없습니다. 바나나가 가리키던 주소값이 변경되는 것 뿐입니다.
console.log(apple); // 🍌
console.log(banana); // 🍎
이 부분이 아래 객체의 메모리 할당과 큰 차이점 입니다.
2) Object 오브젝트형 - Memory Heap !
객체의 경우 원시데이터와 메모리가 저장되는 곳이 다릅니다.
객체는 크기가 일정하지 않은 데이터 이기 때문에 call stack 메모리셀에 바로 저장이 되지 않고, Heap 이라는 곳에 따로 저장이 됩니다.
객체를 Heap에 저장 하면 마찬가지로 주소값이 생기게 됩니다.
그 주소의 값을 call stack 메모리셀에 넣고. 그 주소값을 담은 주소값을 변수에 저장하게 됩니다 ㅎ..
ex) let melon = { name: melon, color : green } ;
1. Heap 에 오브젝트 데이터 값 저장. -> { name: melon, color : green }
2. Heap에 저장된 곳의 주소값 -> Ox1212
3. 그 주소값을 call stack 메모리셀에 전달 & 저장.
4. 주소값이 저장되어 있는 데이터값의 주소 확인. -> Ox0013
5. 해당 주소를 변수 melon 에 저장.
Case 1
let melon = { name: melon, color : green };
let newMelon = { name : melon, color : green };
기존 melon 변수와 동일한 내용을 넣어 새로 newMelon 변수를 생성했습니다.
원시데이터의 경우 값이 같을 경우 동일한 주소값을 참조하여 두 값을 같은 변수로 보았지만, 객체는 다릅니다.
Memory Heap 내부에서 다른 주소값을 가지고 있기 때문입니다.
console.log(!!melon === newMelon); // false
Case 2
let melon = { name: melon, color : green };
let sameMelon = melon;
변수 자체를 할당하는 경우, 메모리 힙에 데이터가 생기는 것이 아니라, 콜 스택 안의 같은 주소값을 참조하게 됩니다.
여기서 오브젝트가 원시데이터와 다른, 주의할 점은.
새로 만들어낸 sameMelon 오브젝트의 내용을 변경하면,
원시데이터의 경우 처럼 새로운 데이터가 생겨나고 sameMelon 값이 새로 생겨난 데이터값의 주소로 변화되는 것이 아니라, (메모리힙에 새로운 객체 데이터를 만드는 것이 아니라)
Heap 내부의 값 자체가 변경되게 됩니다.
sameMelon.name = 'apple' 로 변경한다면
원래 있었던 melon에서도 name값을 호출하면 apple이 반환되게 됩니다.
ex)
sameMelon.name = "apple";
console.log(melon.name) // apple
shallow copy라고도 합니다.
참고사이트