블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

'메모리 누수'에 해당되는 글 2

  1. 2011.03.11 윈도우 메모리 - 2 (malloc 함수)2
  2. 2009.07.30 C/C++ 메모리 오류에 대하여1
2011. 3. 11. 03:20 프로그래밍/Windows API
앞서 언급했던 윈도우 메모리에 관한 이야기 2탄으로 바로 malloc() 함수에 대한 이야기다.

malloc은 뭐 워낙 유명한 함수라서 굳이 이야기 안해도 사용하는 방법쯤은 다 알거라 생각한다. 바로 코드로 들어가 보자.

   
   1:  #include <stdio.h>
   2:  #include <stdlib.h>
   3:  #include <string.h>
   4:   
   5:  #ifdef _DEBUG
   6:  #include <vld.h>
   7:  #include <vldapi.h>
   8:  #endif
   9:   
  10:  void main()
  11:  {
  12:      // Test int
  13:      int *ptr;
  14:      ptr = (int*)malloc(sizeof(int)*10);
  15:   
  16:      for(int i = 0; i < 10; i++)
  17:      {
  18:          ptr[i] = i * 10;
  19:          printf("ptr[%d] = %d\n", i, ptr[i]);
  20:      }
  21:   
  22:      free(ptr);
  23:   
  24:      // Test char
  25:      char *ptr_char;
  26:      ptr_char = (char *)malloc(sizeof(char)*20);
  27:   
  28:      char *str1 = "MEMORY LEAK";
  29:      strcpy(ptr_char, str1);
  30:   
  31:      printf("ptr_char = %s\n", ptr_char);
  32:   
  33:      free(ptr_char);
  34:   
  35:      // Test array of pointers
  36:      int **double_ptr;
  37:   
  38:      double_ptr = (int **)malloc(sizeof(int *)*10);
  39:   
  40:      for(int i = 0; i < 10; i++)
  41:      {
  42:          double_ptr[i] = (int *)malloc(sizeof(int)*10);
  43:          for(int j = 0; j < 10; j++)
  44:          {
  45:              double_ptr[i][j] = (i * 10) + j;
  46:              printf("double_ptr[%d][%d] = %d\n", i, j, double_ptr[i][j]);
  47:          }
  48:      }
  49:   
  50:      for(int i = 0; i < 10; i++)
  51:      {
  52:          free(double_ptr[i]);
  53:      }
  54:   
  55:      free(double_ptr);
  56:   
  57:  }

위의 코드의 제일 윗부분에 라인 6번과 7번을 보면 vld.h 라는 녀석이 있는데 이 녀석은 이른바 visual leak detector라고 불리는 오픈 소스로서 메모리 누수가 발생하면 콘솔 출력창에 어디서 뭐가 왜 어떻게 유출되었는지 상세 정보를 보여주는 아주 착한 놈이다.

이 오픈 소스는 http://www.codeproject.com/KB/applications/visualleakdetector.aspx 에 가면 다운 받을 수 있다.

코드는 세 파트로 나뉘어져 있는데 첫번째 파트에서는 간단하게 10개의 int 메모리를 할당하고 0 에서 9까지 넣어 출력했고 두번째 파트는 char 메모리 공간을 생성하여 MEMORY LEAK라는 섬뜩한 문구를 출력해 봤고 마지막은 이중 포인터를 이용해서 2차원 int 배열을 생성하여 0부터 99까지 각 배열에 대입 후 출력해 봤다.

암튼, 22번 라인, 33번 라인 그리고 50번에서 55번까지 free()로 할당된 메모리를 해제하는 명령들이 나와있는데 메모리는 할당보다 해제가 더 중요하다. 화장은 하는 것보다 지우는 것이 더 중요한 것 처럼... 

예를 들어 두번째 테스트는 Test char 파트에서 free()를 호출 안하면 (라인 33번 지우면), 다음과 같은 에러가 프로그램 종료 후 출력창에 뜬다.


너무나도 친절하게 얼마만큼의 메모리가 누수되었는지 그리고 들어있던 내용은 무엇인지 그리고 게다가 코드의 어느 부분에서 누수가 있었는지 알려준다... 너무 친절해서 눈물이 날 지경이다..

라인 33번에서 55번까지는 이중 포인터의 개념인데 integer 값을 10개 저장하는 배열을 10개 만들었다. 즉, 10 x 10 = 100 개의 int 배열을 만든 셈이다. 중요한 것은 해제인데 해제 할때는 10개의 개별 배열을 모두 하나씩 해제한 후 마지막에 이중 포인터를 해제 한다. 만약 개별 배열들을 해제하지 않고 이중 포인터만 해제해 버리면 10개의 개별 배열은 부모 잃어버린 자식들이 되므로 메모리 누수가 일어난다. 

개별 배열(자식)을 해제 하기 전에 이중 포인터 (부모)를 날려 버리면 자식들을 찾을 수가 없다. 왜냐면 부모 연락처가 없기 때문에... 

malloc을 이용한 할당은 가장 경제적인 메모리 할당이다. 왜냐면 바이트 단위로 사용자가 원하는 만큼 할당하기 때문이다. 좀 있다 살펴볼 다른 메모리 할당 함수는 malloc 처럼 바이트 단위로 할당을 하지 않는다. 경제적인 것은 그만큼 손이 많이 간다. 고로 해제하는 과정을 사용자가 반드시 꼼꼼히 해야 한다는 불편함이 있다. 


posted by 대갈장군
2009. 7. 30. 00:53 프로그래밍
C에서 가장 많이 발생하는 오류가 아마도 메모리 관련 오류가 아닌가 싶은데 이는 여러가지 원인이 존재한다. 여러개의 클래스가 겹쳐지고 상속되고 난리를 치다보면 이중해제를 하는 실수는 누구나 할 수 있다. 

이 문서는 http://www.yolinux.com/TUTORIALS/C++MemoryCorruptionAndMemoryLeaks.html을 참조하여 작성되었다.

1. C/C++에서 메모리 오류의 종류

일단 메모리 공간에 따라 크게 Heap Memory 에러와 Stack (local variables) Memory 에러가 있다. Heap 메모리 영역에서 발생가능한 에러는,

  1. 이미 해제된 메모리 다시 해제 할때
  2. 할당된적도 없는 메모리 해제 할라고 할때
  3. 이미 해제된 메모리 영역에 뭔가 데이터를 쓰려고 할때
  4. 할당된 적이 없는 메모리에 뭔가 데이터를 쓰려고 할때
  5. 메모리 할당 에러
  6. 동적으로 할당된 메모리 배열에서 초과된 index의 위치를 읽거나 쓰려고 할때 

1,2 번과 3,4번을 묶어서 볼 수 있는데 해제시에 발생하는 문제 vs 데이터 입력시 발생하는 문제로 볼 수 있다.

스택 메모리 영역에서 발생가능한 에러들로는

  1. 정적 배열에서 초과된 index의 위치를 읽거나 쓰려고 할때
  2.  함수 포인터 오류인데 잘못된 함수 포인터의 전달로 인한 잘못된 함수 호출


2. 메모리 누수!

메모리 누수는 할당한 동적 메모리를 제대로 해제하지 않고 프로그램을 종료하면 발생하는 것으로써 이것이 지속되면 컴퓨터의 RAM (빠른 메모리 공간)을 모두 소모하여 버리고 Page 파일 (하드 디스크에 생성하는 가상 메모리)를 남발하게 되여 컴퓨터의 성능을 저하시키다 결국 넉다운 된다. 

우선 이 웹사이트에서는 strdup() 함수를 예를 들어 설명하는데 좋은 예인거 같지는 않다. 아무튼 말하고자 하는 점은 malloc() 함수 (C library)로 할당된 메모리는 반드시 free()로 해제하라는 점이다.

요약하자면, 
   1. new로 할당하면 delete로 해제, malloc으로 할당하면 free로 해제.
   2. 할당 실패하면 해제 하지 말것. (할당되지도 않은 놈 해제하면 당연히 에러)
   3. 상속될 가능성이 있는 클래스의 파괴자는 반드시 가상 함수로 선언하라
   4. 포인터 사이에 대입연산 (주소복사)는 항상 메모리 누수 및 이중 해제의 가능성을 가진다.
   5. 클래스의 대입 연산자 사용시 얕은 복사가 잃어난다면 (주소만 복사) 역시나 누수와 이중해제의 위험 존재.

3. 메모리 부패 (Memory Corruption)

참 단어 선택이 오묘한데 부패라고 하니 꼭 이상한 듯 들리지만 오염보다는 나은것 같아서 그렇게 표현했다. 한국어로 뭐라해야 할지 모르겠다.

Memory Corruption은 사용자가 부주의하여 명확한 할당 없이 주소에 접근하여 데이터를 변경하려 하거나 잘못된 주소 위치로 접근하려 하는 경우를 말한다. 즉, 컴퓨터 입장에서 보면 메모리가 '부패'한 셈이다 :)

우선 흔히 발생하는 Buffer Overflow의 경우는 할당된 메모리 양보다 더 많은 데이터를 집어 넣을때 발생하는 것이고 또 다른 흔한 Memory Corruption은 할당한 배열을 초과하는 위치에 접근하는 경우다. (인덱스 초과)

그리고 또 흔한 경우로 포인터 변수로 선언한 변수에 new로 메모리를 할당하지도 않았는데 포인터 주소가 가지는 멤버 변수나 함수로 접근하는 경우다. 당연히 에러다.

free() 함수로 날려버린 변수에 접근하는 것 역시 '부패한 메모리'로의 접근이다. 

생각보다 중요한 것이 나오는데 new []로 배열을 할당한 경우 반드시 delete []로 삭제해야 한다. 이거 의외로 실수하는 경우가 많다.

함수를 호출하여 함수 내에서 선언된 지역 변수의 주소를 넘겨와 사용할 경우 그 지역 변수는 함수 리턴 된 후에 언제든 사라질 수 있는 위험을 안고 있다. 그래서 절대로 이렇게 사용하면 안된다는 점.. 명심하자.











posted by 대갈장군
prev 1 next