. 9.9.0 동적메모리 할당 서문
본문 바로가기
프로그래밍 공부/CSPP

9.9.0 동적메모리 할당 서문

by 불냥이_ 2021. 1. 15.

가상 메모리의 영역을 저수준의 mmap과 munmap의 함수를 사용하여 생성하고 삭제할 수 있다.

 

 

/* mmap과 munmap은 어떤 함수인가? 기본적이고 간단한 가상메모리 함수인가?

 mmap은 사용자 수준에서 커널 내부 주소 접근시 사용하고 munmap은 해제할 때 사용한다고 한다. */

 

하지만 대개 추가적인 가상메모리를 런타임에 획득할 필요가 있을 때, 동적 메모리 할당기를 사용하는 것이 더 선호된다고 한다. 

 

동적 메모리는 힙heap 이라고 하는 프로세스의 가상메모리 영역을 관리한다.

/* 동적 메모리에 들어가기에 앞서 간단히 메모리의 영역을 공부하고 넘어가겠다. 

메모리 구조를 알아보자 (velog.io)

 

메모리 구조를 알아보자

프로그램이 운영체제로부터 할당받는 대표적인 메모리 공간(RAM)은 다음과 같다.

velog.io

메모리의 영역

 그림을 차근히 보자. low memory와 high memory가 있다.

아마 low memory부터 채우고 점점 올라가는 형식인 것 같다.

 

 우선 가장 밑단에는 코드 영역이 있다.

이 영역에는 우리가 코딩해서 작성한 명령어들이 저장되어 있고, 런타임동안 CPU는 여기서 코드를 하나씩 읽어가면서 실행할 것이다.

 

그 다음은 데이터 영역이 있다.

 런타임동안 계속 쓰게되는 전역 변수와 정적 변수, 문자열 상수등이 여기에 들어간다.

/* 그렇기 때문에 전역 변수보다는 지역 변수를 쓰는 것이 선호되는 것이 아닐까? 지역 변수는 함수 안에서만 돌지만 전역 변수는 코딩이 끝날 때까지 메모리 한 곳을 차지하게 됨으로서 효율이 떨어진다. 동적 할당도 같은 이론일 것이다. */

 

 

 이 두 영역이 컴파일하자마자 결정되는 메모리의 영역이다. 그렇기에 우리는 왜 코딩을 효율적으로 해야하는지, 전역 변수의 사용을 최소화해야하는지 알 수 있다. 바로, 사용가능한 메모리에 직결적으로 연결되는 문제이기 떄문이다.

 

이어서 힙 영역으로 들어가보자.

 

 힙 영역은 사용자가 직접 관리하는 영역이다. 

힙 영역에서는 사용자에 의해 메모리 공간이 동적으로 할당되고 해제된다. (malloc과 free를 기억하라. 그리고 이 프로젝트에서 할 malloc 함수가 할 일이다.)

 

그리고 힙으로 할당하면 메모리의 low -> high로 주소가 할당되져간다.

 

 동적으로 할당하기 때문에 런타임동안 개체의 개수(와 크기)가 알 수 없는 경우에 사용한다.

(예를 들어 트리에서 노드의 갯수가 사용자 임의대로 설정되는 경우에 노드를 동적 할당해주어야 우리는 대응할 수 있을 것이다.)

또한 개체가 너무 커서 스택 할당자에 맞지 않는 경우 사용가능하다고 한다. (그렇다면 stack은 기본적으로 배치되는 영역의 크기가 작나?)

 

하지만, 힙은 할당과 해체에 시간이 걸리고, 할당을 잘못했을 경우에 힙이 오염될 수 있다.

 

또한, 다중의 스레드thread가 동시에 힙 상의 한 데이터에 접근하려고하면 힙 경합에서 문제가 날 수 있다.

 

 

그렇다면 스택은 어떤가?

 

 스택은 함수 호출시에 따라오는 지역 변수와 매개변수가 저장되는 영역이다.

함수의 호출과 동시에 할당되며 호출이 끝나면 소멸한다. 이렇게 스택 영역에 저장되는 함수의 호출 정보를 스택 프레임stack frame이라고 한다.

 

 스택은 heap과 반대로 high memory에서 low로 진행한다. 이는 스택은 함수가 반환했을 때, 바로 반환해야 함으로 제일 high부분에 설치하며, 스택이 high->low로 진행하는 이유에는 두가지가 있는데, 첫번째는 heap에 쌓인 데이터들과 겹치는 것을 최대한 방지하기 위해서인데,

 

 만약에 heap이 얼마나 쌓일지 모르기때문에 stack 중간 부분부터 high로 진행한다면 할당받을 수 있는 메모리가 상당히 제한될 것이다. 그래서 heap에게 최대한 많이 할당해주기 위해서 high에서 low로 진행한다. 

 

 두번째는 스택이 low->high로 진행하면 stack overflow가 발생했을 때, 커널을 벗어나게 되므로 차라리 heap을 오염시키는 게 낫다. 그래서 high에서 low로 진행된다.

 

 스택은 컴파일 때 영역 크기가 저장되어 있으며 이 덕분에 빠른 접근 속도를 가지지만 넣을 수 있는 데이터에 한계가 있 다는 단점도 가지게된다.

 

 스택과 힙은 서로 같은 공간에 존재하기 때문에 스택/힙의 영역이 점점 커지면 서로의 영역을 침범할 수도 있다.

heap가 stack을 침범하면 heap overflow, stack이 heap을 침범하면 stack overflow 가 발생한다. */

 

 

 데이터와 힙의 경계는 top of the heap이 있고, 이를 가리키는 brk(break) 포인터가 있다.

할당기는 힙을 블록들의 집합으로 관리한다. 할당기는 두 유형이 있는데, 두 유형 모두 명시적으로 할당하도록 요구하지만, 반환할 때는 차이가 있다. 아래는 그 차이에 관한 것이다. 

 

1번 유형 : Explicit Allocator (명시적 할당기)

 Explicit Allocator 는 명시적으로 할당된 블록을 반환하도록 요구한다. C언어의 free함수와 C++의 delete가 명시적 반환 요구의 예이다.

 

2번 유형 : Implicit Allocator (묵시적 할당기)

 이는 자동으로 사용자가 함수로서 명시적 반환을 요구하지 않아도 알아서 사용하지 않는 메모리를 반환하도록 하는 것이다. 묵시적 할당기는 가비지 컬렉터 garbage collector 로도 알려져 있다.

 

 

이번 프로젝트에서 할 것은 명시적 할당기의 설계와 구현이다. (시간이 된다면 묵시적도 시도해볼 것이다.)

 

 

댓글