디버거
디버그하려는 다른 프로세스에 붙여서 사용하는 프로그램.
-g
옵션을 전달하면 최종 실행 가능한 목적 파일에 디버깅 정보가 삽입된다.gdb에 관한 것
gdb
는 디버깅 명령어를 전달하는 커맨드 라인 인터페이스를 갖는다.r(또는 run)
을 입력b(또는 break)
명령어로 지정n(또는 next)
: 코드의 다음 행을 실행하는 명령어x/4b @@@
: @@@이 가리키는 지역에서 4바이트를 나타냄 @@@는 배열의 첫번째 원소를 가리키는 포인터임.x/8b @@@
: @@@이 가리키는 지역에서 8바이트를 나타냄전역 변수는 데이터나 BSS같은 다른 세그먼트에 할당되며 이들 세그먼트는 스택 세그먼트처럼 작동하지 않는다.
스택 메모리의 비슷한 영역보다 힙 메모리 영역에 할당하는 것이 더 느리다.
힙의 특성
1. 힙은 자동으로 할당되는 메모리 블록을 갖지 않는다!
2.힙은 메모리 크기가 크다
3. 힙 메모리 내에서 메모리의 할당과 해제는 개발자가 관리.
4. 힙에 할당된 변수는 스코프를 전혀 갖지 않는다.
5. 변수를 언제 해제해야 할지도 모르며, 효율적으로 메모리를 관리하려면 메모리 블록의 스코프와 소유자를 위한 새로운 정의를 생각해야함.
6. 힙 메모리 블록의 주소를 지정하려면 포인터만 사용할 수 있다.
7. 힙 세그먼트는 소유자 프로세스 전용이므로 검사하려면 디버거를 사용해야한다.
헤더 파일 <stdlib.h>에 정의
힙 메모리 블록 얻기 : malloc(메모리 할당), calloc(메모리 할당 및 초기화), realloc(이미 할당된 메모리 블록의 크기를 조정해 메모리를 재 할당)
힙 메모리 해제: free
realloc 함수: 이 전의 블록에 있는 데이터를 변경하지 않으며 이미 할당된 블록을 새로운 블록으로 확장. 단편화 때문에 현재 할당된 블록을 확장할 수 없을 때 다른 충분히 큰 블록을 찾은 뒤 이전 블록에서 새 블록으로 데이터를 복제한다.
해제에 실패하면 메모리 누수가 발생
valgrind(메모리 프로파일러 중 하나)로 메모리 누수 감지하기
-g
옵션 으로 예제를 빌드
valgrind로 실행해야함.
주어진 실행 가능한 목적 파일을 실행하는 동안 valgrind는 모든 메모리 할당 및 해제를 기록, 마지막으로 실행이 종료되거나 충돌이 발생했을 때, valgrind는 할당 및 해제에 대한 요약과 해제되지 않은 메모리양을 출력
--leak-check=full
옵션을 valgrind를 실행할 때 전달하면, 힙 메모리의 누수에 관련된 코드가 나타난다.
힙 메모리 블록은 아무 스코프도 갖지 않는다. -> 수명이 불분명, 다시 정의되어야함.
힙 수명의 복잡성을 극복하기 위해 고안된 전략 중 가장 좋은(그러나 완벽한 해법은 아닌) 것은, 메모리 블록의 소유자를 정의하는 방법.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int front;
int rear;
double* arr;
} queue_t;
void init(queue_t* q)
{
q->front = q->rear = 0;
// 여기에서 할당된 힙 메모리 블록은 큐 객체가 소유
q->arr = (double *)malloc(Q_MAX_SIZE * sizeof(double));
}
void destroy(queue_t* q)
{
free(q->arr);
}
.
.
.
.
int main(int ac, char **av)
{
// 여기에서 할당된 힙 메모리 블록은 main 함수가 소유
queue_t* q = (queue_t *)malloc(sizeof(queue_t));
.
.
.
return (0);
}
위 코드는 각각 특정 객체를 소유하는 서로 다른 2개의 소유권을 포함.
첫 번째 소유권, 포인터 arr가 주소를 지정하는 힙 메모리 블록에 관한 것, 큐 객체가 소유하는 queue_t구조체에 있음. 큐 객체가 존재하는 한, 이 메모리 블록은 할당된 채로 남아있어야 한다.
두 번째 소유권, main 함수가 가지는 힙 메모리 블록을 큐 객체인 q의 자리 표시자로 간주, 이 큐 객체는 main 함수 자신이 소유하는 것.
큐 객체와 main 함수가 소유하는 힙 메모리 블록을 구분하는 일은 중요. 이 블록 중 하나를 해제하더라도 다른 블록은 해제되지 않기 때문
어떤 개체(객체나 함수)가 힙 메모리 블록을 소유한다면 주석으로 표기한다는 점
같은 힙 메모리 블록을 여러 번 해제하면 이중 해제(더블 프리)가 발생
소유권 전략 이외에도 가비지 컬렉터(프로그램에 내장된 자동 메커니즘, 그 어떤 포인터도 주소를 가리키지 않는 메모리 블록을 수집) 사용가능. C에서는 사용 X(객체가 파괴되었다는 정보를 전달받을 수 없음) C++에서는 소멸자를 이용해 이 기술을 효과적으로 사용
캐싱이란? 두 데이터 저장소가 서로 읽기/쓰기 속도가 다를 때 컴퓨터 시스템의 많은 부분에서 사용되는 모든 비슷한 기법을 가리키는 일반적인 용어
느린 저장소에서 다른 항목을 로드해야 하는 어떤 한 항목을 처리한다고 가정,
지금 캐시 내부에 있을, 최근에 가져온 여러 버킷 중에서 필요한 항목을 검색
-> 캐시에서 항목을 찾았다면 = 적중(hit)
-> 캐시 저장소에 항목이 없다면 느린 저장소로 가야하고, 캐시 메모리 내에서 다른 항목들에 대한 버킷을 읽어야함 = 실패(miss)
캐시가 더 적중할 때 마다 성능은 더 좋아진다.