[WEEK 11] Three Easy Pieces - 17. 메모리 관리 API

신호정 벨로그·2021년 10월 16일
0

Today I Learned

목록 보기
57/89

메모리 관리 API

어떻게 메모리를 할당하고 관리해야 하는가

17.1 메모리 공간의 종류

C 프로그램이 실행되면, 두 가지 유형의 메모리 공간이 할당된다.

첫 번째는 스택(stack) 메모리라고 불리며 할당과 반환은 프로그래머를 위해 컴파일러에 의해 암묵적으로 이루어진다.

void func() {
	int x;
}

예를 들어 func()라는 함수 안에서 x라 불리는 정수를 위한 공간이 필요하다면 함수 안에서 정수 x를 선언한다.

컴파일러가 나머지 작업을 수행하여, func()가 호출될 때 스택에 공간을 확보한다.

함수에서 리턴하면 컴파일러는 프로그래머 대신에 메모리를 반환한다.

함수 리턴 이후에도 유지되어야 하는 정보는 스택에 저장하지 않는 것이 좋다.

오랫동안 값이 유지되어야 하는 변수를 위해 힙(heap) 메모리라고 불리는 두 번째 유형의 메모리가 필요하다.

모든 할당과 반환이 프로그래머에 의해 명시적으로 처리된다.

void func() {
	int *x = (int *)malloc(sizeof(int));
}

이러한 경우 한 행에 스택과 힙 할당이 모두 발생한다.

우선 컴파일러가 포인터 변수의 선언(int *x)을 만나면 정수 포인터를 위한 공간을 할당해야 한다는 것을 안다.

프로그램이 malloc()을 호출하여 정수를 위한 공간을 힙으로부터 요구한다.

malloc()은 그 정수의 주소를 반환한다.

반환된 주소는 스택에 저장되어 프로그램에 의해 사용된다.

17.2 malloc() 함수

malloc() 함수의 호출은 힙에 요청할 공간의 크기를 넘겨 주면, 성공했을 경우 새로 할당된 공간에 대한 포인터를 사용자에게 반환하고 실패했을 경우 NULL을 반환한다.

#include <stdlib.h>

void *malloc(size_t size);

malloc()의 인자는 size_t 타입의 변수이고 이 변수는 필요 공간의 크기를 바이트 단위로 표시한 것이다.

double *d = (double *)malloc(sizeof(double));

malloc() 호출에서는 정확한 크기의 공간을 요청하기 위하여 sizeof() 연산자를 사용한다.

C 언어에서 sizeof()는 통상 컴파일 시간 연산자이다.

sizeof()는 숫자로 대체되어 malloc()에 전달된다.

int *x = malloc(10 * sizeof(int));

printf("%d\n", sizeof(x));

첫 번째 행에서, 정수형 우너소 10개를 가지는 배열을 위한 공간은 선언한다.

다음 행에서 sizeof() 연산자를 사용하면 64비트 컴퓨터에서 8의 값을 반환한다.

이러한 경우 sizeof()는 동적으로 할당받은 메모리의 크기가 얼마인지가 아니라, 정수를 가리키는 포인터의 크기가 얼마인지 물어본다고 생각하기 때문이다.

int x[10];

printf("%d\n", sizeof(x));

이러한 경우 변수 x에 40바이트가 할당되었다는 것을 컴파일러가 알 수 있는 정적인 정보가 충분한다.

17.3 free() 함수

할당된 메모리를 언제, 어떻게 해제하고 해제 여부를 확인하는 것은 중요하다.

더 이상 사용되지 않는 힙 메모리를 해제하기 위해 free()를 호출한다.

int *x = malloc(10 * sizeof(int));

free(x);

한 개의 인자로 malloc()에 의해 반환된 포인터를 받는다.

할당된 영영의 크기는 전달되지 않는다.

할당된 메모리의 크기는 메모리 할당 라이브러리가 알고 있어야 한다.

17.4 흔한 오류

메모리 할당 잊어버리면 세그멘테이션 폴트(segmentation fault)가 발생한다.

메모리를 부족하게 할당받으면 버퍼 오버플로우(buffer overflow)가 발생한다.

할당받은 메모리 초기화하지 않으면 초기화되지 않은 읽기가 발생한다.

메모리 해제하지 않으면 메모리 누수(memory leak)가 발생한다제

반복적으로 메모리 해제하기: 프로그램은 가끔씩 한 번 이상 해제하는 문제를 이중 해제(double free)라 부른다.

프로세스가 종료하면 왜 메모리 누수가 일어나지 않는가

시스템이 두 단계로 메모리를 관리하기 때문이다.

메모리 관리의 첫 번째 단계는 운영체제에 의해 수행된다.

프로세스가 실행할 때 메모리를 프로세스에게 건네 주고 프로세스가 종료하거나 다른 이유로 죽을 때 메모리를 되돌려 받는다.

두 번째 단계는 각 프로세스 내에서 malloc()과 free()를 호출할 때 힙 내에서 수행된다.

free()를 호출하지 못하여 메모리가 누수되었더라도 프로세스가 종료할 때, 운영체제는 프로세스의 모든 메모리를 회수한다.

프로세스의 코드, 스택, 힙 등 모든 페이지가 포함된다.

프로세스 주소 공간의 힙이 어떤 상태가 되더라도, 운영체제는 프로세스가 죽을 때 모든 페이지를 회수한다.

사용자가 해제하지 않았더라도 메모리가 누수되지 않는다.

17.5 운영체제의 지원

malloc 라이브러리가 프로세스 가상 주소 공간 안의 공간을 관리하지만 라이브러리 자체는 시스템에게 더 많은 네모리를 요구하고 반환하는 시스템 콜을 기반으로 구축된다.

brk라고 불리는 시스템 콜은 break는 힙의 마지막 위치를 나타내는 프로그램의 break 위치를 변경하는 데 사용된다.

brk는 새로운 break 주소를 나타내는 한 개의 인자를 받는다.

새로운 break가 현재 break보다 큰지 작은 지에 따라 힙의 크기를 증가시키거나 감소시킨다.

sbrk는 증가량 만을 받아들이는 것을 제외하고 비슷한 용도로 사용된다.

mmap() 함수를 사용하여 운영체제로부터 메모리를 얻을 수도 있다.

올바른 인자를 전달하면 mmap()은 프로그램에 anonymous의 메모리 영역을 만든다.

anonymous 영역은 특정 파일과 연결되어 있지 않고 스왑 공간(swap spapce)에 연결된 영역을 의미한다.

17.6 기타 함수들

calloc()은 메모리 할당 영역을 0으로 채워서 반환한다.

realloc()은 이미 할당된 공간에 대해 추가의 공간이 필요할 때 유사용된다.

realloc()은 더 큰 새로운 영역을 확보하고 이전 영역의 내용을 복사한 후에 새 영역에 대한 포인터를 반환한다.

0개의 댓글