Memory Leak인듯 한데요.. 소스코드 보시고 조언좀 부탁드립니다

rainblow의 이미지

클래스의 멤버로 가지고 있는 다른클래스의 포인터에 new로 생성한 클래스인스턴스를 할당했다가 새로운 클래스로 그값을 대치하려고 합니다.
이를 저는 linked list에서 멤버 교체할때처럼 하면 된다고 생각했는데,
메모리가 줄질 않는군요.
코드보시면 아시겠지만 destructor에 printf한값들은 전부 찍히고 있습니다..뭔가 제가 잘못이해한 부분이 있는듯 한데..
코드보시고 조언좀 부탁드립니다. 환경은 RH8.0 + gcc3.2 입니다.

아래 코드의 ⓐⓑⓒ에서의 메모리값을 top명령어로 본것을 옮겨보면
ⓐ : 24848 24M 720 S 1.7 2.4 0:00 GwMain
ⓑ : 48884 47M 720 S 1.7 4.7 0:00 GwMain
ⓒ : 48888 47M 724 S 0.0 4.7 0:00 GwMain

입니다.
제 생각으로는 delete를 한후인 ⓒ 에서는 다시 24M로 돌아와야 하는게 아닌가 하거든요.. 어디가 잘못된걸까요?

#include <iostream>

#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#include <string>
#include <map>

using namespace std;

class
GwRecord {

public:

  GwRecord () {
            requestLen   = 0     ;
            request[0]   = '\0'  ;

            resultLen    = 0     ;
            result[0]    = '\0'  ;

            resultSepLen = 0     ;
            resultSep[0] = '\0'  ;

            memset (request,    0, sizeof(request)   );
            memset (result,     0, sizeof(result)    );
            memset (resultSep,  0, sizeof(resultSep) );

            printf("GwRecord constructed!! %x \n", this);
        }

  ~GwRecord() {   
                  printf(" Destructor GwRecord  %x \n",this);
        }

        char             request[8192];
        int              requestLen;
        char             result[8192];
        int              resultLen;
        char             resultSep[8192];
        int              resultSepLen;

};
class
GwServer { 
public:
  GwServer () {
            makeMemory();
        }
    
  ~GwServer() {
            deleteMemory();
            printf("  Destructor GwServer  \n");
        }
};

class
GwMain {
public:

      GwServer* m_server;
      GwMain()
      {
      }
      ~GwMain()
      {
      }

};

int
main (int argc, char* args[]) {

    GwMain* Main = new GwMain();
    string x;
    cout << "Before Make GwServer()" << endl;
    cin >> x;

    GwServer* oldServer = new GwServer ();
    Main->m_server = oldServer;
    cout << "After Make GwServer() and assign to server" << endl;
    //ⓐ
    cin >> x;

    GwServer* newServer = new GwServer ();
    cout << "After Make new GwServer()" << endl;
    cin >> x;

    GwServer* temp = Main->m_server;
    cout <<  "After assign old GwServer() to temp" << endl;
    cin >> x;
   //ⓑ

    Main->m_server = newServer;
    delete temp;
    cout <<  "After delete temp" << endl;
    //ⓒ
    cin >> x;

    return 0;
}
doldori의 이미지

대원칙은 new와 delete의 짝이 맞아야 한다는 겁니다. 코드를 보면
new는 3번 했는데 delete는 1번만 했군요. 당연히 릭이 납니다.
그리고 자원의 획득과 해제를 클래스 사용자 측에서 과도하게 부담하는
것 같습니다. 뭔가 더 좋은 방법이 있을 텐데요.

rainblow의 이미지

안녕하세요 지난번 비슷한 질문에도 답변주셨던분 같네요.
우선 답변 감사드리구요.
저 파일을 컴파일해서 돌려보면 마지막 c 에서 초기에 먹었던 a와 같은 양의 메모리를 점유하는게 아니고, 실제로 보면 b 상태의 메모리를 그대로 유지합니다.
말씀하신것처럼 new와 delete는 쌍을 이루어야 하지만, 목적상 메모리상에 클래스객체 한개는 항상 유지하고자 하는 목적이거든요.

요약을 하면

1. new -> 24M 메모리 생성.
2. 또 new -> 24*2 => 48M 메모리 점유
3. 먼저 생성한 놈 delete => 24M로 메모리 감소

가 정상일듯 한데 여전히 48M를 가진다는 것이죠.
문제는 바로 요점입니다. 이게 24M로 줄어야 정상이겠죠.
저는 GwRecord가 생성되어 점유되는 메모리에 관심있어서 테스트를 하는 중이라 GwMain같은 놈은 delete안했습니다만, 이건 일단 논외로 하구요..

님께서 일전에 답장주신대로 boost를 쓸려고 해도 이게 제가 첨부터 코딩한게 아니라서 고친다는것 자체가 부담이고, 현재 있는 문제점 자체가 궁금해서.. 그냥 모르는채로 넘어가긴 힘드네요.

한번만 더 짚어주시면 감사하겠습니다.

cdpark의 이미지

delete 후에 메모리를 OS에 돌려줘야 하는 건 아닙니다. 라이브러리가 가지고 있다가 다음번 new 할당 때에 쓸 수 있습니다. (대개 그러고요.)

익명 사용자의 이미지

http://valgrind.kde.org/faq.html

4.3 참고하십시오.

doldori의 이미지

cdpark wrote:
delete 후에 메모리를 OS에 돌려줘야 하는 건 아닙니다. 라이브러리가 가지고 있다가 다음번 new 할당 때에 쓸 수 있습니다. (대개 그러고요.)

라이브러리 자체에서 따로 메모리 관리 정책을 쓰지 않는 한 delete를 하면
OS로 회수되어야 하는 거 아닌가요? 저도 이 점은 확실히 모르겠습니다.
이런 이유가 아니라면 GwServer의 소멸자가 가장 혐의가 짙군요.
makeMemory()와 deleteMemory()가 제대로 동작하는지 살펴보세요.
rainblow의 이미지

class
GwServer {
public:
  GwServer () {
            makeMemory();
        }

  ~GwServer() {
            deleteMemory();
            printf("  Destructor GwServer  \n");
        }

        void            makeMemory()
        {
              for(int i=0; i< 1000; i++)
              {
                  GwRecord*   newRcd = new GwRecord ();
                  records[i] = newRcd;
              }
        }

        void            deleteMemory()
        {
            map<int, GwRecord*>::iterator it;
            for (it = records.begin(); it != records.end(); it++)
            {  
               delete it->second;
            }
            records.clear();

        }
        map <int, GwRecord*  >  records;
};

네 제가 생각하기에도 라이브러리에서 메모리를 가지고 있는다는건 좀..
보통 자바나 닷넷의 경우에는 버추얼머신에서 메모리관리를 하니까 그럴수 있다고 하지만, 이경우는 좀 다르지 않나 하는 생각입니다.
제가 코드를 붙여넣으면서 빼먹었더군요.
GwServer의 제대로된 클래스소스는 위와 같습니다.

소스를 실행시켜보면 GwServer의 소멸자에서 deleteMemory를 호출하고 여기서 delete를 호출하는데 따라서 GwRecord의 소멸자가 전부 호출되고 있습니다. 생성자에서 찍은 메모리 주소와도 1:1로 맵핑이 되고요..

고수님들의 의견 부탁드립니다.

rainblow의 이미지

Quote:
4.3. My program uses the C++ STL and string classes. Valgrind
reports 'still reachable' memory leaks involving these classes
at the exit of the program, but there should be none.

First of all: relax, it's probably not a bug, but a feature. Many
implementations of the C++ standard libraries use their own memory pool
allocators. Memory for quite a number of destructed objects is not
immediately freed and given back to the OS, but kept in the pool(s) for
later re-use. The fact that the pools are not freed at the exit() of
the program cause Valgrind to report this memory as still reachable.
The behaviour not to free pools at the exit() could be called a bug of
the library though.

Using gcc, you can force the STL to use malloc and to free memory as
soon as possible by globally disabling memory caching. Beware! Doing
so will probably slow down your program, sometimes drastically.

- With gcc 2.91, 2.95, 3.0 and 3.1, compile all source using the STL
with -D__USE_MALLOC. Beware! This is removed from gcc starting with
version 3.3.

- With 3.2.2 and later, you should export the environment variable
GLIBCPP_FORCE_NEW before running your program.

There are other ways to disable memory pooling: using the malloc_alloc
template with your objects (not portable, but should work for gcc) or
even writing your own memory allocators. But all this goes beyond the
scope of this FAQ. Start by reading
http://gcc.gnu.org/onlinedocs/libstdc++/ext/howto.html#3 if you
absolutely want to do that. But beware:

1) there are currently changes underway for gcc which are not totally
reflected in the docs right now ("now" == 26 Apr 03)

2) allocators belong to the more messy parts of the STL and people went
at great lengths to make it portable across platforms. Chances are
good that your solution will work on your platform, but not on
others.

위에 어느분이 링크를 걸어주신 부분에서 해당하는 부분을 읽어봤습니다.
이게 사실이라면 제가 그동안 잘못알아왔던것이 맞겠네요
STL같은 라이브러리 차원에서 메모리를 풀링한다..
참.. 어색한 개념입니다만..
그렇다면 제가 몇일동안 고생한게 보람이 없어지는 허탈한 경우네요..

보통 메모리 체크는 어떤 방법으로 하세요?
저처럼 top명령어로 하시지는 않는가보죠?

doldori의 이미지

이건 지금과는 좀 다른 얘기 같습니다. 그러니까

vector<int> v;
v.clear();

에서 clear()를 해도 v가 갖고 있던 메모리가 바로 해제되지는 않는다는 얘깁니다.
allocator로 메모리 관리를 제어할 수 있는 길을 열어 두고는 있긴 하지만요.
지금처럼 날포인터(raw pointer)로 직접 해제하는 것과는 다르지요.
혹시 GwServer의 map을 생각하고 계실지 모르겠는데 그것도 GwServer의
소멸자가 끝나면 map 개체 자체도 소멸되므로 당연히 메모리가 회수되어야
합니다.
그런데 올리신 코드를 WinXP에서 .NET과 gcc로 해봤는데 잘 되는데요? :)
(c)에서 24M의 메모리가 회수되는 것을 확인했습니다.

jj의 이미지

doldori wrote:
이건 지금과는 좀 다른 얘기 같습니다. 그러니까

vector<int> v;
v.clear();

에서 clear()를 해도 v가 갖고 있던 메모리가 바로 해제되지는 않는다는 얘깁니다.
allocator로 메모리 관리를 제어할 수 있는 길을 열어 두고는 있긴 하지만요.
지금처럼 날포인터(raw pointer)로 직접 해제하는 것과는 다르지요.
혹시 GwServer의 map을 생각하고 계실지 모르겠는데 그것도 GwServer의
소멸자가 끝나면 map 개체 자체도 소멸되므로 당연히 메모리가 회수되어야
합니다.
그런데 올리신 코드를 WinXP에서 .NET과 gcc로 해봤는데 잘 되는데요? :)
(c)에서 24M의 메모리가 회수되는 것을 확인했습니다.

전 C++는 잘 모릅니다만, Spec에 raw pointer를 delete하면, 메모리가 확실히 반환된다라고 되어 있지 않는다면, STL이 아니라 raw pointer로 delete한다고 해도 반환될지는 확실히 보장 못할 것 같습니다만.(gcc와 visual C++에선 된다고 해도) STL까지 가지 않고서도, 스펙이 강제하지 않는다면 new, delete도 메모리를 재활용할 수 있을듯 싶습니다.

이유는 모르겠지만 순간적인 반환상태가 그렇게 중요하다면 system call정도를 믿을 수 있지 않을까 생각됩니다.

--
Life is short. damn short...

doldori의 이미지

jj wrote:
전 C++는 잘 모릅니다만, Spec에 raw pointer를 delete하면, 메모리가 확실히 반환된다라고 되어 있지 않는다면, STL이 아니라 raw pointer로 delete한다고 해도 반환될지는 확실히 보장 못할 것 같습니다만.(gcc와 visual C++에선 된다고 해도) STL까지 가지 않고서도, 스펙이 강제하지 않는다면 new, delete도 메모리를 재활용할 수 있을듯 싶습니다.

맞는 말씀입니다. 표준에서 메모리 회수 시점을 강제하는 구절은 찾지 못했습니다.
저의 이전 포스팅은 정확하지 않음을 사과드립니다.
rainblow의 이미지

저는 RedHat8.0 에서 GCC3.2버전으로 테스트 했습니다만,
확실히 c 에서 24M가 반환되지 않습니다.

운영체제에 따른 영향이라고 밖에는 볼수가 없겠군요.
RH9.0 시스템에 있는데 그곳에서 해봐도 같은 결과군요...

쩝.. 운영체제에 따른 문제일까요? ㅡㅡ;;

추신 : 바로 윗글을 읽지 못했군요. Spec에 그런 부분이 없다면 메모리가 재활용될수도 있는거군요.. 많은걸 배웁니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.