Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com Message-ID: <20020914194429.961.qmail@web10003.mail.yahoo.com> Date: Sat, 14 Sep 2002 12:44:29 -0700 (PDT) From: Tirth Sanyal Subject: Memory Leak - DLL problem To: cygwin AT cygwin DOT com Cc: tsanyal AT yahoo DOT com MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Hi, I have been encountering a strange memory leak problem in my code which has prompted me to write a small program to isolate and demonstrate it. I have pasted the code at the bottom of the email. Please cc your replies to tsanyal AT yahoo DOT com Here is the problem: Platform - Windows 2000 SP3, Cygwin, gcc - 2.95.3-5 - In a DLL say libfoo, I define and export a class Base and also export a function "foo" whose interface is the following: Base* foo(void) - "foo" instantiates an object of the class Base and returns a pointer to this object. - In my application (which has the main routine) I load the dynamic library libfoo, call function "foo" in it , get the pointer to the newly created object and immediately call "delete" on this pointer. - The destructor function of the class Base contained in the libfoo DLL is called. - This is done about 2 million times. - However, when I see the memory usage, it rises to about 32MB and stays there... it never goes down which clearly indicates that the memory is not being freed. - I have also created another test program which does not involve any dynamic loading of libraries. This is a self contained monolithic program which defines and implements the class in the same file. - Instantiation and destruction of class Base objects are done 2 million times in this program as well. - The memory consumption is only a meagre 1 MB. This indicates that there is a problem with the freeing of memory corresponding to objects when classes are exported in a DLL. I would appreciate it if you could share your ideas regarding this matter. Thanks in advance. -Tirth CODE - pasted ( foo.hpp , foo.cpp , main.cpp) Followed by (main.hpp and main.cpp of the monolithic program) ------------------------------------------------------- libfoo - DLL foo.hpp /* Only include this header file once. */ #ifndef _FOO_H_ #define _FOO_H_ 1 #include #ifdef EXTERN_FOO #undef EXTERN_FOO #endif # ifdef _LIBFOO_COMPILATION_ # define EXTERN_FOO __declspec(dllexport) # else # define EXTERN_FOO __declspec(dllimport) # endif extern "C" { class EXTERN_FOO Base { public: char* str; Base(void); ~Base(void); }; EXTERN_FOO Base* foo (void); } #endif /* !_FOO_H_ */ ------------------------------------------------------- foo.cpp #if HAVE_CONFIG_H # include #endif #define _LIBFOO_COMPILATION_ # include "foo.hpp" #undef _LIBFOO_COMPILATION_ #include #include EXTERN_FOO Base::Base(void) { str = (char*) malloc(1000); } EXTERN_FOO Base::~Base() { if (str) { free(str); printf("Deleted the base Object\n"); } } Base* foo(void){ Base* base = NULL; base = new Base(); printf("foo:Created a base Object\n"); return (base); } ------------------------------------------------------ APPLICATION main.cpp #include #include #include #include #include #ifndef EXIT_FAILURE # define EXIT_FAILURE 1 # define EXIT_SUCCESS 0 #endif #include #ifndef PATH_MAX # define PATH_MAX 255 #endif #include /* This is missing from very old Linux libc. */ #ifndef RTLD_NOW # define RTLD_NOW 2 #endif typedef Base* entrypoint (); //typedef u_char* entrypoint (); /* Save and return a copy of the dlerror() error message, since the next API call may overwrite the original. */ static char *dlerrordup (char *errormsg); int main (int argc, char *argv[]) { char modulepath[1+ PATH_MAX] = ""; char *errormsg = NULL; void *module = NULL; entrypoint *run = NULL; int errors = 0; int result = 0; int i = 0; char* j = NULL; Base* base = NULL; // u_char* base = NULL; printf("---------------------------------------------------------------\n\n"); if (argc != 2) { fprintf (stderr, "USAGE: newtest MODULENAME \n"); exit (EXIT_FAILURE); } /* Set the module search path. */ strcat (modulepath, "/usr/local/lib/"); strcat (modulepath, argv[1]); printf("Modulepath is : %s\n",modulepath); /* Load the module. */ printf("Loading module %s........",argv[1]); module = dlopen (modulepath, RTLD_NOW | RTLD_GLOBAL); if (!module) { errors = 1; printf("Failed to load module\n"); } /* Find the entry point. */ if (!errors) { printf("loaded\n"); printf("Finding entry point for 'foo'........"); run = (entrypoint*) dlsym (module, "foo"); /* In principle, run might legitimately be NULL, so I don't use run == NULL as an error indicator. */ errormsg = dlerrordup (errormsg); if (errormsg != NULL) errors = dlclose (module); else printf("found\n"); } /* Call the entry point function. */ if (!errors) { printf("Calling entry point function 'foo'........\n"); for (i = 0; i < 2000000; i++){ base = (*run) (); if(!base) errormsg = strdup ("module entry point execution failed"); delete (base); } } /* Unload the module, now that we are done with it. */ if (!errors) errors = dlclose (module); if (errors) { /* Diagnose the encountered error. */ errormsg = dlerrordup (errormsg); if (!errormsg) { fprintf (stderr, "%s: dlerror() failed.\n", argv[0]); return EXIT_FAILURE; } } printf("Terminating demo\n"); if (errormsg) { fprintf (stderr, "%s: %s.\n", argv[0], errormsg); free (errormsg); return EXIT_FAILURE; } sleep(20); return EXIT_SUCCESS; } /* Be careful to save a copy of the error message, since the next API call may overwrite the original. */ static char * dlerrordup (char *errormsg) { char *error = (char *) dlerror (); if (error && !errormsg) errormsg = (char*) strdup (error); return errormsg; } ------------------------------------------------------- Monolithic example main.hpp #ifndef _MAIN_HPP_ #define _MAIN_HPP_ 1 class Base { public: char* str; // int i; Base(void); virtual ~Base(void); }; class Derived: public Base { public: Derived(void); virtual ~Derived(void); }; #endif ------------------------------------------------------- main.cpp #include #include #include #include #include "main.hpp" Base::Base() { str = (char*) malloc(1000); } Base::~Base() { if(str) free(str); } Derived::Derived() { } Derived::~Derived() { } int main (int argc, char *argv[]) { Base* base = NULL; // u_char* derived = NULL; int i = 0; for(i=0;i<20000000;i++){ base = new Base(); // derived = (u_char*) new Derived(); printf("Object created\n"); delete(base); // delete((Base*) derived); printf("Object deleted\n"); } sleep(20); return 1; } ---------------------END------------------------------ __________________________________________________ Do you Yahoo!? Yahoo! News - Today's headlines http://news.yahoo.com -- Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple Bug reporting: http://cygwin.com/bugs.html Documentation: http://cygwin.com/docs.html FAQ: http://cygwin.com/faq/