본문 바로가기
스터디/C++

[ C++ ] new 와 delete를 이용한 메모리 동적할당, 메모리 누수(Memory Leak)에 대하여..

by 알 수 없는 사용자 2019. 9. 6.

우선 동적 할당이란

" 프로그램 실행 중에 동적으로 메모리를 할당하는 것을 말합니다."

 

즉, 런타임 중에 메모리를 할당한다는 것이고, 동적으로 할당된 메모리는 Heap영역에 할당이 됩니다.

 

프로그램을 개발하다 보면 상황에 따라 메모리 공간을 실시간으로 확보해야 하는데

그때 사용하는 게 메모리 동적 할당입니다.

 

C에서는 malloc로 힙 영역에 할당해주고, free로 힙에 할당된 메모리 공간을 소멸합니다.

C++에서는 new로 힙 영역에 할당해주고, delete로 힙에 할당된 메모리 공간을 소멸합니다.

 

하지만 C++에서는 C언어의 헤더 파일을 추가하는 것도 가능하기 때문에 malloc와 free도 사용이 가능합니다.

그러면 먼저 malloc와 free를 이용한 메모리 동적 할당 예제를 보겠습니다.

 

 

 

char * str = (char *)malloc(sizeof(char)* len);

-> (char *) = malloc의 반환형은 void *이므로 형 변환을 해줍니다.

-> sizeof(char)은 char을 바이트 크기로 연산해주는 연산자입니다.

-> (sizeof(char) * len) len만큼 곱하여 원하는 크기의 메모리를 할당합니다.

 

그리고 free를 이용하여 할당한 메모리를 해제해줍니다.

 

 

그럼 malloc와 free를 쓰면 되지, 왜 new와 delete가 있을까?

 

우선, C에서의 malloc와 free의 불편한 점이 있었습니다.

 

- 할당할 대상의 정보를 무조건 바이트 크기 단위로 전달해야 한다. 

malloc(n)

 

- malloc의 반환형이 void형 포인터이기 때문에 앞에 적절한 형 변환을 거쳐야 한다. 

  int * i = (int *)malloc(sizeof(int))

 

 

그러나 C++에서 제공하는 new와 delete를 사용하면 해결이 됩니다.

int * ptr = new int;
double * ptr = new double;
int * arr = new int[3];
double * arr = new double[7];

그리고 소멸할 때는

delete ptr
delete arr

하면 됩니다.

 

그래서 void형 포인터를 반환하는 malloc와 달리 new는 해당 객체에 맞는 형식의 포인터로 반환해 줍니다.

 

그럼, 아까 한 예제를 new와 delete로 바꿔보겠습니다.

 

짠. 더 간결해진 것을 알 수 있습니다.

 

사실 C++에서는 malloc와 free를 사용하지 않고, malloc와 free를 사용합니다. 

그 이유는 객체를 생성할 때 문제가 될 수 있습니다.

 

다음을 한 번 살펴보자.

출력 결과를 보면 new로 객체를 생성할 경우에는 생성자가 호출되었지만,

malloc는 생성자가 호출되지 않는 것을 볼 수 있습니다.

 

또한, new를 이용해 객체를 생성하면 초기 값도 줄 수 있지만

malloc는 불가능합니다.

 

그래서 앞으로 C++에서 메모리 동적 할당을 하게 되면 new와 delete를 사용할 것을 권장합니다.

 

 

 

 

추가로 Java와 C#, Python을 사용한 입장으로써 궁금했던 점을 적어보기 전에.

 

일단

C/C++의 동적 할당의 가장 중요한 점은 할당한 메모리를 꼭 해제해줘야 한다는 점이다.

C/C++은 이렇게 동적으로 할당된 메모리를 free와 delete로 소멸시켜야 한다.

 

하지만 JAVA, C#, Python, Objective-C에는 가비지 콜렉터(Garbage Collector) 기법을 이용하여

동적으로 할당한 메모리 중 필요 없는 영역을 소멸시킵니다.

 

그럼 정말 GC에 의존해도 되는 걸까?

 

 

그전에 이것을 먼저 고민해보자.

C와 C++에서 free, delete로 메모리를 소멸시키지 않는 경우는 어떤 문제가 생길까?

 

만약 메모리를 할당하고 나중에 소멸시키지 않으면 시스템의 메모리를 고갈시키는

메모리 누수(Memory Leak) 문제가 발생한다.

 

그러면 단순히 시스템을 다시 시작하여 코드를 수정하여 문제를 해결하면 되지 않나? 싶을 수도 있는데,

한 번 시스템이 가동되면 지속적으로 가동해야 하는 시스템에서는 매우 치명적인 문제를 일으킨다.

 

주로 서버에서 이런 일이 큰 문제가 되는데, 실제 모 회사 서버에서 메모리 누수 문제를 해결하지 못해서 지속적으로

재부팅을 하고 있던 경우가 있다.

 

또 다른 예를 들면, 검색 관련된 클래스의 객체를 생성했다고 하자.

그럼 사용자가 검색을 할 때마다 메모리 상에 객체가 올라가고 지속된다면..

프로그램이 죽을 뿐만 아니라 해당 프로그램이 구동되는 시스템까지 죽이는 최악의 경우가 발생한다.

 

결국, 메모리 누수가 발생하면

" 프로그램이 종료되어도 해당 메모리 공간을 다시 사용할 수가 없으며,

이런 메모리 누수가 누적되면 결국 시스템 전체의 메모리 부족 현상이 발생할 수 있습니다. "

 

그래서 메모리를 자동으로 관리해주는 가비지 콜렉터(Garbage Collector) 기법이 생겨났다.

 

 Java, C#, Python, Objective-C 등 다양한 언어에서 이미 이용하고 있는 기법이다.

그럼 처음에 들었던 의문으로 돌아가 보자.

 

정말 GC에 의존해도 문제가 없을까?

 

상식적으로 GC가 작동된다면 사용하지 않은 메모리는 자동으로 반환해야 되지만

실제로 메모리가 반환되지 않고 계속 늘어나기도 합니다.

 

이렇게 메모리가 계속 늘어나는 이유가 뭘까?

 

가비지(Garbage)는 더 이상 참조가 없는 메모리를 뜻합니다.

메모리가 반환되지 않고 계속 늘어나고 있다면 어디선가 반환될 메모리를 의도치 않게 참조하고 있어서 

GC가 반환 메모리로 인식하지 않아 청소를 못하고 자꾸 쌓이고만 있는 것입니다.

 

 

※ 그래서 가비지 콜렉터(Garbage Collector) 기법이 있더라도 GC에 너무 의존하지 말고 메모리 관리에 신경 써야 한다.

'스터디 > C++' 카테고리의 다른 글

[ C++ ] 파일 분할  (0) 2019.09.20
[ C++ ] C언어의 표준함수 호출  (0) 2019.09.06
[ C++ ] Call-by-value , Call-by-reference  (0) 2019.09.05
[ C++ ] const ( 상수 )의 활용  (0) 2019.09.05
[ C++ ] 인라인(inline) 함수  (0) 2019.09.05