Develope/MFC2013. 6. 24. 10:26

출처 : 알 Blog


프로그래밍을 하는 것은 결국 메모리를 가지고 노는 일이다.

 

메모리가 없으면 기껏 프로그램을 짜봐야 아무의미없는 일이 된다.

 

프로세스가 실행되기 위한 최소한의 요소를 어딘가에 적재해두고 돌려야 할건데 그게 없으면?

 



 

이거보다 좀 더 심각한 문제라고 생각하자.

 

하여간 프로그래밍과 메모리는 뗄레야 뗄 수 없는 관계다...

 

 

DOS 시절에는 1MB도 안되는 640KB 정도의 메모리로 각종 프로그램을 만들었다.

 

지금은 그와 비교도 안되게 발전하여 자그마치 GB 단위의 메모리를 사용 가능하다.

 

그렇다면 하나의 프로세서가 사용 가능한 메모리의 공간은 얼마나 될까?

 



 

작업 관리자 오른쪽 아래의 할당된 메모리 부분을 보자.

 

대략 5GB 정도의 메모리를 사용할 수 있다.

 

물리적인 RAM 의 용량과 상관없이 이 OS에서 사용할 수 있는 메모리의 총 량이다.

 

그렇다면 개발자가 이 메모리를 다 잡아두고 쓰면 메모리 관리하는 것이 편할거 같다는 생각이 든다.

 

머리 복잡하게 정적이니 동적이니, new 니 delete 니, malloc, free 등등 이런거 쓸 필요가 없지 않은가?

 

그래서 정적 배열에다 char 형 크기 1조 Byte(1 TB) 할당을 시도해보았다.

 

#define MAX 1000000000000 

int main()

{

    char sMemtest[MAX];

    return 0;

}

 

그러면 결과는?

 

error C2148: total size of array must not exceed 0x7fffffff bytes

 

아! 배열의 크기는 0x7FFFFFFF 를 넘지 못한다고 컴파일러가 오류를 출력한다.

 

그러면 21억만 안넘기면 되겠네?

 

#define MAX 2100000000 

int main()

{

    char sMemtest[MAX];

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

    sMemtest[MAX-1] = 2;

    printf("value %d, number %d\n", sMemtest[MAX-1], MAX);

    return 0;

}

 

과연... 컴파일을 했을 시는 오류가 발생하지 않는다.

 

하지만 실행하면?

 



 

어? 잘 뜬다?

 

배열의 가장 마지막 자리에 값을 넣어보고 출력하니 정상적으로 출력이 된다.

 

그 말인 즉슨 배열 크기가 약 21억(2의 32승의 반, 즉 2의 31승)까지 잡히는군! 

 

이라고 생각하면 큰일난다.

 

이게 바로 Visual Studio 의 함정이라고 할 수 있는 것인데, 얼핏 보기에는 21억이라는 숫자가 정상적으로 잡히는 것 같지만

 

사용자의 실제 사용 가능 메모리는 5 GB 정도밖에 안된다!

 

직접 배열의 하나하나하나에 값을 넣어보면 분명 탈이 발생한다.

 

#define MAX 2100000000 

int main()

{

    char sMemtest[MAX];

 

    for(i=MAX-1; i>0; i--)
   {
      sMemtest[i] = 1;
    } 

    for(i=MAX-1; i>0; i--)
   {
      printf("value %d, number %d\n", sMemtest[i], i);
    }

    return 0;

}

 

위의 코드를 실행시켜보면 얼핏 봐서는 될 것 같지만 막상 실행해보면 문제가 생겼으니 디버깅을 하라는 메세지가 뜬다.

 



 

그냥 싱숭생숭해서 파일명은 모자이크 처리했다.

 

단순 문법적으로는 오류가 없지만 배열에 하나하나 값을 집어넣으면 오류가 생긴다.

 

사실 이건 안되는게 정상이고 되는게 이상한거다.

 

배열을 선언하면 그 배열은 스택 공간에 잡히게 되는데 시스템에서 그 정도 크기의 스택을 잡을 수 있다면

 

굳이 힙이 따로 있을 필요가 없기 때문이다. 하여간 이 부분은 밑에서 다시 보겠다.

 

그렇다면 이렇게 정적으로 선언할 수 있는 크기는 얼마나 될까?

 

컴파일러는 0x7FFFFFFF 까지의 크기에 대해서는 OK 하니 직접 숫자 넣어가면서 확인하는 수 밖에 없었다.

 

그 결과 약 1MB(1,000,000) 의 크기라면 정상 동작한다는 것을 확인했다. (1MB 는 1024 X 1024 이기에 100만보다는 조금 크다.)

 

#define MAX 1000000

int main()

{

    char sMemtest[MAX];

 

    for(i=MAX-1; i>0; i--)
   {
      sMemtest[i] = 1;
    } 

    for(i=MAX-1; i>0; i--)
   {
      printf("value %d, number %d\n", sMemtest[i], i);
    }

    return 0;

}

 

결과

 



 

정상적으로 잘~ 뜬다.

 

그렇다면 정적으로는 1MB 정도의 크기가 한계인건가? 더 이상은 불가능한가?

 

까짓거 1MB 배열을 천 개 정도 선언 해주면 1GB 잡아버리는거 아닌가...

 

근데 그건 그 것대로 안된다.

 

#define MAX 1000000

int main()

{

    char sMemtest[MAX];

    char sMemtest2[MAX];

    for(i=MAX-1; i>0; i--)
   {
      sMemtest[i] = 1;
    } 

    for(i=MAX-1; i>0; i--)
   {
      sMemtest2[i] = 1;
    }

    return 0;

}

 

이렇게 하면 다른 배열이니까 될 것 같지만 또 이거 뜬다.

 



 

결국 정적 메모리 - STACK 에 잡을 수 있는 메모리의 총 량이 약 1M 정도밖에 안된다는 말이다!

 

심지어 int a[10000]; 을 추가해도 위와 같은 문제가 생긴다.

 

다 합쳐본 결과 약 1034000 Byte 크기의 메모리 공간이 한계인 듯 싶다. (1024 * 1024 = 1048576)

 

이렇게 스택에 정적으로 할당할 수 있는 메모리가 1MB 밖에 안되기 때문에 힙에다가 동적으로 메모리를 할당하는 것이다.

 

 

정적 메모리 할당의 최대치를 확인했으니 이번에는 동적 메모리 할당의 최대치를 확인해보자.

 

#define MAX 4200000000

int main()

{

    char *pFD;
    pFD = new char[MAX];
    delete[] pFD;

    return 0;

}

 

일단 42억으로 설정해두고 컴파일해보니 역시나 error C2148: total size of array must not exceed 0x7fffffff bytes 에러가 발생했다.

 

어찌되었든 간에 약 21억 Byte 이상의 크기를 가진 배열을 선언하려고 하면 컴파일러가 딴지를 거는 것이다.

 

그래서 21억으로 바꾸고 컴파일을 해보니 정상적으로 컴파일이 되었다.

 

21억 = 2,100,000,000 = 2.1 GB 라는 것인데...

 

실행을 해보았다.

 



 

프로그램 만든넘이 바보짓했다고 한다...

 

아 동적으로 메모리를 할당한다고 해도 2GB는 불가능하구나...

 

컴파일러는 OK 하는데?

 

비주얼 스튜디오가 분명 뛰어난 컴파일러는 맞지만 전지전능한 신은 아니다. 어쨌든간에 기록된 문법에 맞으니까 맞다고 한거겠지.

 

그래서 조금 줄여서 2GB 를 할당해보니까 되었다.

 

#define MAX 2000000000

int main()

{

    char *pFD;
    pFD = new char[MAX];
    delete[] pFD;

    return 0;

}

 

메모리 할당되는 현황을 살펴보았다.



 

약 2000M (실제로는 1935M) 가 할당되었다가 정상 해제되었다.

 

그렇다면 동적 메모리 할당에서도 최대로 할당해줄 수 있는 메모리 크기가 2 GB 인 것일까? 앞서 정적의 1MB 처럼?

 

확인해보았다.

 

int main()

{

    char *pFD;
    char *pSD;

    pFD = new char[2000000000];
    pSD = new char[100000000];

    delete[] pSD;
    delete[] pFD;

    return 0;

}

 

첫 번째의 2 GB는 할당이 되는데...

 

두 번째를 100 MB 를 할당하려는 순간!

 



 

찍-

 

역시나 안된다.

 

즉 하나의 프로세스에서 가질 수 있는 동적 메모리의 최대 크기는 약 2 GB 인 것이다.

 

마지막으로 해본 테스트는 다음과 같다.

 

만약 2 GB 크기의 메모리를 동적 할당하는 프로세스를 2 개 이상을 실행시켰을 경우는 어떻게 될까?

 

 int i;
 int **pI = NULL;
 int *pn = NULL;

 try
 {
  pn = NULL;   // NULL 값을 delete 해도 아무 상관 없다. 중요함. 밑에서 할당하다 실패해서
 }                  // 할당을 중단 했는데 할당한 양보다 delete 를 과하게 해도 되는 이유
 catch (bad_alloc xa) // 그렇다고 선언했던 메모리보다 많이 지우려고 시도했다가는 작살남
 {
  cout<<"fail"<<endl;
 }
 delete[] pn;
 cout<<"Allocation Start"<<endl;

 try 
 {
  pI = (int **) new int* [10];
  cout << "Allocation Success" << endl;
 }
 catch (bad_alloc xa) 
 {
  cout << "Allocation Failure" << endl;
  return -1;
 }

 for(i=0; i<10; i++)
 {
  try 
  {
   *(pI+i) = new int[100000000]; // 100MB X 4 크기의 int 배열 동적 할당 (int = 4 Byte)
   cout << "Allocation Success Number : " << i << endl;
  } 
  catch (bad_alloc xa) 
  {
   cout << "Allocation Failure Number : " << i << endl;
   getchar();
   return -1;
  }
 }

 

 cout << "Allocation Done" << endl;

 

 for(i=0; i<10; i++)
 {
  delete[] *(pI+i);
 }
 delete[] pI;

 

하나의 프로세스가 최대 2 GB 까지 할당 받을 수 있으니 현재 약 4 GB 의 메모리 공간이 남아있기에 

 

대략 2개 정도의 프로세스가 돌아갈 것으로 생각된다.

 



 

세 번째 프로세스에서 할당을 받으려고 하니 아래 메세지가 뜨면서 페이징 파일의 크기가 늘어났다.

 



 

메모리를 할당하려고 하니 남은 메모리가 없어서 OS가 자동으로 페이징 파일 크기를 늘려 메모리를 할당하는 것이다.

 

이 경우 임시적으로 메모리의 최대 크기가 늘어나는 효과가 있다. (물론 물리적인 RAM 을 사용하는 것보다는 속도가 느리다.)

 

아래의 그림으로 확인하자.

 

이전 작업 관리자

 



 

페이징 파일 크기가 늘어난 작업 관리자

 



 

마지막으로 네 번째 프로세스에서 다시금 동적으로 메모리 할당을 받으려고 하니 이제 페이징으로 변환 가능한 메모리의 범위를

 

넘어버렸는지 더 이상은 할당 자체가 불가능했다.

 

 

위의 실험 결과를 통해 한 프로세스가 할당 받을 수 있는 (확보할 수 있는) 메모리 크기는 다음과 같음을 알 수 있다.

 

 

할당할 수 있는 메모리의 크기

주의점

정적 메모리 할당

약 1 MB (스택 영역)

한 프로세스 내에서 총 1 MB 를 넘지 못한다.

동적 메모리 할당

약 2 GB (힙 영역)

한 프로세스 내에서 총 2 GB 이상을 넘지 못한다.

 

그러니까 new 쓰면 delete 를 필수로 써줘서 사용한 메모리는 제때 제때 삭제해줘야한다.

 

new 신나게 쓰고 delete 안해줘서 메모리가 줄줄 새다가 2 GB 를 넘어가면 더 이상 메모리 할당도 안되니까 그 때는 정말 답이 없다.

 

아무리 유저의 컴 사양이 날고 긴다고 해도 개발자가 메모리 관리 삽질하면 GG 란 거지...

 

주의하자... 나중에 써야지 하다가 까먹어 버리지 말고,

 

new 를 써서 메모리를 할당했으면 그 즉시 밑에다가 delete 를 써두자!


Posted by AsCarion