UNIX 시스템은 실행 중인 프로세스에게 4GB의 가상 메모리 공간을 할당하는데, 상위 1GB는 커널이, 하위 3GB는 사용자 프로그램이 차지한다.
프로그램이 자동으로 사용하는 메모리 영역으로 함수 호출과 관계되는 지역변수와 매개변수가 저장된다. 함수 호출 시 생성되며, 함수가 끝나면 반환된다. stack 사이즈는 각 프로세스마다 할당되지만 프로세스가 메모리에 로드될 때 stack 사이즈가 고정되어 있어 런타임 시 stack 사이즈를 바꿀 수 없다. 명령 실행 시 자동으로 증가 또는 감소하기 때문에 보통 메모리의 마지막 번지를 지정한다.
필요에 의해 메모리를 동적으로 할당할 때 사용하는 메모리 영역으로 동적 메모리 영역이라고 부른다. C 에서 malloc() calloc() 등의 함수를 사용하여 메모리 크기를 할당할 수 있으며, 메모리 주소 값에 의해서만 참조되고 사용되는 영역이다.
위의 stack과 heap영역은 사실 같은 공간을 공유한다. heap이 메모리의 낮은 주소부터 할당되면 stack은 높은 주소부터 할당되는 식이다. 그래서 각 영역이 상대 공간을 침범하는 일이 발생할 수 있는데 이를 각각 stack overflow, heap overflow 라고 한다.
프로그램이 실행될 때 생성되고 프로그램이 종료되면 시스템에 반환되며, 전역변수, 정적변수, 배열, 구조체 등이 저장된다. Data 영역은 다시 BSS 영역과 Data(GVAR) 영역으로 나누어지는데, 초기화된 데이터는 Data 영역에 저장되고, 초기화되지 않은 데이터는 BSS 영역에 저장된다.
텍스트 영역은 실행 명령을 포함하는 코드들이 들어가는 부분이다.
프로그램을 시작 할 때 컴파일한 프로그램(기계어)이 저장되어 있고, 읽기 전용 영역이기에 프로세스가 함부로 변경 할 수 없고 변경 시 오류를 발생시킨다.
// 초기 함수 구현
init(stackSize,heapSize){
maxHeapSize = heapSize;
maxStackSize = stackSize;
for (var i = 0; i < (stackIdx / 2); i++){
heap.set(i ,{'size' : 0,'type': "", 'address': 21});
}
for (var i = 20; i >= (stackIdx / 2); i--){
stack.set(i ,{
'name' : "",
'size': 0,
'address': heapIdx
});
}
}
#include<iostream>
void foo(){
int a;
int b;
};
int main(){
int a;
double b;
foo();
bar();
return 0;
}
1) 메인 함수가 실행되어 int a; double b;가 실행되는 모습을 스택으로 표현
2) foo()함수가 실행되는 모습(리턴까지)을 스택으로 표현
만약 이때 리턴이 실행된다면 주황색으로 감싸진 모든 스택이 없어집니다.
call(name, paramCount){
stackIdx -= (paramCount + 2);
nowStackSize += 8 + 4 * (paramCount + 1);
if (nowStackSize <= maxStackSize){
stack.set(stackIdx,{'name' : name, 'size': nowStackSize, 'address': -1});
} else{
console.log("Heap is Full");
}
}
returnf(name){
let re = [];
for(var i = 20; i >= stackIdx; i--){
let st = stack.get(i)['name'];
if (st != ''){
if (st === name){
break
}
re.push(i)
}
}
stack.set(i,{'name': '', 'size': 0, address: -1})
return re[re.length-1];
}
malloc(type, count){
let typeSize = sz.get(type)
typeSize = Math.ceil(typeSize / 8) * 8 * count
nowHeapSize += typeSize;
if (nowHeapSize <= maxHeapSize){
heapIdx += 1;
heap.set(heapIdx,{'size': nowHeapSize, "type":type,'address': stackIdx});
stack.get(stackIdx)["address"] = heapIdx
} else{
console.log("Heap is Full");
}
}
call - return이 있듯이, malloc은 free 함수를 통해 종료합니다. - 해당 영역에 값을 reset
free(pointer){
const addr = stack.get(pointer)['address'];
nowHeapSize -= heap.get(addr)['size'];
if ( addr > -1) {
heap.set(addr,{'size': 0, type: "",'address': 21});
}
else{
console.log("malloc함수를 실행한 적이 없습니다.!!");
}
}
메모리 관리 기법 중 하나로 프로그램이 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역을 해제하는 기능이다. 즉, 동적 할당된 영역 가운데 어떤 변수도 가리키지 않는 메모리 영역을 탐지 하여 자동으로 해제 하는 기법입니다.
대표적으로 c에서는 malloc()으로 동적할당하였지만 스택에서 리턴(return)하여 그 값이 더 이상 쓸모없어졌을 경우(free를 하지 않았기 때문에 힙영역에 값이 살아있지만 그 값을 스택에서 참조 할 수 없음) gc를 통해 그 값을 다시 리셋시킬 수 있다.