이름의 유래 : 메모리에서 힙과 스택을 나누는 경계선을 break 또는 break point라고 한다
brk()를 호출하면 데이터 세그먼트의 마지막 브레이크 포인터를 end로 지정한 주소로 설정
sbrk()를 호출하면 데이터 세그먼트를 increment만큼 늘린다(음수, 양수 가능)
익명 메모리 메핑
glib은 버디 메모리 할당 기법 이둉(메모리 할당은 데이터 세그먼트와 메모리 매핑 이용)
malloc()을 구현하는 전통적인 방법은 데이터 세그먼트를 2의 배수만큼의 구역으로 나눈 다음 요청하는 크기에 가장 가까운 크기의 구역 반환
인접 구역이 비어 있을 경우 하나로 합쳐 더 큰 구역을 확보하거나 힙의 윗 부분이 완전히 비어있다면 brk()를 이용해 브레이크 포인트를 낮춰 힙을 줄임
버디 메모리할당 기법은 속도와 명료함이 장점이지만 2종류의 메모리 파편화가 발생
내부 파편화 : 메모리 할당 요청을 충족시키기 위해 요청보다 더 많은 메모리를 반환 할 때
외부 파편화 : 요청을 만족 시킬 메모리가 남아 있지만 인접하지 않은 두 구역으로 떨어져 있을 때
상기 이유로 대규모 메모리 할당에는 glib은 힙을 사용하지 않고 익명 메모리 매핑사용(파일 기반 매핑과 유사하나 파일과 연관되지는 않음)
익명 메모리 맵핑을 단일 할당만을 위한 새로운 힙(힙 영역 밖에 위치)
장점 : 파편화 X, 크기조정/권한설정/힌트 사용 가능, 힙관리 필요 없음
단점 : 크기는 페이지 크기의 정수배로 메모리 낭비 될 수 있음, 매핑을 생성하는 부하가 힙보다 크다
익명 메모리 맵핑 생성하기
익명 메모리 메핑을 사용하거나 독자적인 메모리 할당 시스템을 작성 시 수동으로 익명 메모리 매핑 생성
mmap()으로 메모리 매핑 생성, munmap()으로 매핑 해제
MAP_ANONYMOUS 플래그로 익명으로 매핑 생성, MAP_PRIVATE 플래그로 공유되지 않도록
커널이 COW로 애플리케이션의 익명 페이지를 0으로 채운 페이지로 매핑하므로 0을 채울 필요가 없다.
/dev/zero 매핑하기
BSD 간은 다른 유닉스 시스템에는 MAP_ANOYMOUS 플래그가 없어 /dev/zero파일을 매핑하는 방식 사용
고급 메모리 할당
1
2
3
#include<malloc.h>intmallopt(intparam,intvalue);
mallopt()를 다른 메모리 할당 인터페이스를 사용 전 호출하여 할당 연산을 인자로 제약
mallopt()를 호출하면 param으로 지정된 메모리 관리와 관련된 인자를 value로 설정
호출이 성공하면 0이 아닌 값을 반환, 실패시 0을 반환(errno는 설정하지 않음)
param의 목록은 다음과 같다
M_CHECK_ACTION : MALLOC_CHECK_ 환경변수 값
M_MMAP_MAX : 최대 매핑 계수 설정
M_MMAP_THRESHOLD : 힙 대신 익명 맵핑으로 처리할 할당요청의 임계 크기
M_MXFAST : 패스트 빈의 최대 크기(힙 내 특수 메모리 영역)
M_PERTURB : 메모리 포이즈닝 활성화(미리정의한 값으로 채워서 관리 에러 탐지)
M_TOP_PAD : 데이터 세그먼트 크기 조정 시 사용되는 패딩의 크기
M_TRIM_THRESHOLD : glib이 sbrk()를 호출해서 메모리를 커널에 반환하기 전 데이터 세그먼트의 빈 메모리의 최소 크기
malloc_usable_size()와 malloc_tirm()으로 튜닝하기
malloc_usable_size()는 메모리 영역의 실제 할당 크기 반환(요청보다 할당 영역이 클 수 있음)
malloc_tirm()은 유지되어야 하는 패딩 바이트를 제외한 가능한 많은 데이터 세그먼트를 줄이고 1반환
두 함수를 쓸일은 적다(이식성도 떨어짐)
메모리 할당 디버깅
프로그램은 MALLOC_CHECK_ 환경 변수를 설정해서 메모리 서브시스템의 고급 디버깅 기능을 활성 가능
환경 변수로 디버깅을 제어하므로 재 컴파일은 필요 없이 실행만 하면 됨
환경 변수를 1로 설정 시 유익한 정보를 담은 메세지가 stderr로 출력, 2로 설정하면 프로그램은 즉시 abort()를 호출해서 종료
1
$ MALLOC_CHECK_=1 ./rudder
통계 수집하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<malloc.h>structmallinfo{intarena;/* malloc이 사용하는 데이터 세그먼트 크기 */intordblks;/* 비어 있는 메모리 블록 수 */intsmblks;/* 패스트 빈 수 */inthblks;/* 익명 매핑 수 */inthblkhd;/* 익명 매핑 크기 */intusmblks;/* 전체 할당 최대 크기 */intfssmblks;/* 사용 가능한 패스트 빈의 크기 */intuordblks;/* 전체 할당 공간의 크기 */intfordblks;/* 사용 가능한 메모리 블록의 크기 */intkeepcost;/* 정리가 가능한 공간 크기 */}structmallinfomallinfo(void);
mallinfo()를 호출하면 mallinfo 구조체에 통계를 담하 변환(포인터가 아니라 값으로 반환)
메모리 관련 통계를 stderr로 출력하는 malloc_stats() 함수도 제공
스택기반 할당
1
2
3
#include<alloca.h>void*alloca(size_tsize);
alloca()은 호출이 성공시 size 바이트 만큼 할당한 메모리에 대한 포인터 반환
상기 메모리는 스택에 위치하며 실행중인 함수가 반환 될때 자동으로 해제
일부 구현에서는 호출이 실패할 경우 NULL반환, 대부분 실패하지 않거나 실패를 알리지 못함(실패는 스택 오버플로를 일으킴)
alloca()는 버그가 많고 일관성 없는 구현으로 나쁜 평판을 얻어서 호환성을 위해서는 사용하지 않는 것이 좋으나 malloc() 보다 월등히 성능이 좋다.
작은 메모리 할당이 필요하고 성능향상이 필요할 경수 사용
스택에서 문자열 복사하기
문자열을 임시로 복사하는 경우 alloca()을 매우 자주 사용(책에 예제 있음)
리눅스 시스템은 스택에 문자열을 복사하는 stddup()함수군을 제공, 그러나 POSIX는 상기 함수를 정의하지 않으므로 이식성이 필요한 경우 주의
가변 길이 배열
C99는 컴파일 시점이 아니라 실행중에 배열의 크기를 결정하는 가변 길이 배열(VLA)를 도임(GNU C는 이미 지원하고 있었음)
가변 길이 배열은 alloca()와 마찬가지로 동적메모리 할당이라는 부하를 없애 줌
alloca()로 얻은 메모리는 함수 주기동안 유지, 가변 길이 배열로 얻은 메모리는 변수가 스코프를 벗어 날때 까지만 유지
1
2
3
4
for(i=0;i<n;++i){charfoo[i+1];/* foo 사용 */}
메모리 할당 메커니즘 선택하기
메모리 조작하기
C언어는 메모리 바이트 조작 함수 제공, 사용자가 제공한 버퍼 크기에 의존하며 에러 반환을 안함(잘못된 영역을 넘기면 세그멘테에션 폴트 발생)