Subject: Memory Leak - DLL problem
From: Tirth Sanyal <[email-masked]>
To: [email-masked]
Cc: [email-masked]
Date: Sat, 14 Sep 2002 12:44:29 -0700 (PDT)

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 [email-masked]

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 <sys/types.h>
#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 <config.h>
#endif

#define _LIBFOO_COMPILATION_
#  include "foo.hpp"
#undef _LIBFOO_COMPILATION_

#include <stdio.h>
#include <stdlib.h>

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 <foo.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef EXIT_FAILURE
#  define EXIT_FAILURE        1
#  define EXIT_SUCCESS        0
#endif

#include <limits.h>
#ifndef PATH_MAX
#  define PATH_MAX 255
#endif

#include <dlfcn.h>
/* 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 <foo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#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/