Discussion:
memory leak detection techniques
(too old to reply)
David Webber
2007-10-10 17:29:46 UTC
Permalink
I have a small memory leak which is driving me mad!

I have done the usual DEBUG_NEW stuff in all the files of the MFC exe and
DLLs. that usually does the trick but this time it isn't telling me about
it.

The problem may be in a non-MFC DLL. Any suggestions for how to find it?

[Years ago I had Bounds Checker but it got to be too expensive upgrading it
every time a new version of Windows became current, but DEBUG_NEW has been
ok for me ever since.]

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Norbert Unterberg
2007-10-10 18:53:34 UTC
Permalink
Post by David Webber
I have a small memory leak which is driving me mad!
I have done the usual DEBUG_NEW stuff in all the files of the MFC exe
and DLLs. that usually does the trick but this time it isn't telling me
about it.
The problem may be in a non-MFC DLL. Any suggestions for how to find it?
How do you know about the leak? Do you see the memory leak dump in the IDE's
output window when your application exits?
If yes then you have a good chance. You can set a memory breakpoint on the
allocation ID that is listed in the memory leaks dump. Add a call to
_CrtSetBreakAlloc(<put the ID here>); at the beginning of main()

Norbert
David Webber
2007-10-10 20:37:33 UTC
Permalink
Post by Norbert Unterberg
How do you know about the leak? Do you see the memory leak dump in the IDE's
output window when your application exits?
Yes.
Post by Norbert Unterberg
If yes then you have a good chance. You can set a memory breakpoint on the
allocation ID that is listed in the memory leaks dump. Add a call to
_CrtSetBreakAlloc(<put the ID here>); at the beginning of main()
Thank you! I have never needed that before. I now know where the leak
is, but not why.

But the plot thickens:

In my non-MFC DLL I have a global instance of an object of the form

CMyCollection : public std::vector<CMyThing>;

Its normal constructor is called on startup, which just creates and empty
collection.
It is only called once.
It is filled in (also once) with a method of CMyColllection schematically of
the form

CMyCollection::FillIn()
{

CMyThing thing;

for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}

It is usually not changed, and lasts until the program shuts down.
It is never copied. (It has a copy constructor, because I feel nervous
without them, but it is never called.)
The destructor is called once, and just calls clear() (because I feel it
is neat to do so).

I am being told that the memory leak is associated with the CMyThing's copy
constructor invoked by push_back() above.

How can that be?

Even stranger, I have had all of this present in the code for years and a
memory leak has only been reported recently.

Any ideas gratefully received.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Giovanni Dicanio
2007-10-10 22:05:52 UTC
Permalink
Hi Dave,
Post by David Webber
CMyCollection : public std::vector<CMyThing>;
[...]

Could you please try to use a Has-A "embedding" relationship (not Is-A via
inheritance), i.e. : putting a private/protected instance of
std::vector<CMyThing> into CMyCollection.
Post by David Webber
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
[...]
Post by David Webber
I am being told that the memory leak is associated with the CMyThing's
copy constructor invoked by push_back() above.
How can that be?
It would be interesting to see some code for CMyThing... (maybe it has got
some non-smart pointers that are not correctly copied, or it hasn't got a
virtual destructor that would be required...).

Also, you should pay attention about the C/C++ run-time used by each module
(DLLs and EXE).
I think it should be the same (e.g. debug multithreaded DLL, or
multithreaded DLL for release builds).
Post by David Webber
Even stranger, I have had all of this present in the code for years and a
memory leak has only been reported recently.
Maybe you are now using VS2005 which implements better checking for memory
leaks than VC6 or 2003...

Ciao
Giovanni
Giovanni Dicanio
2007-10-10 22:32:05 UTC
Permalink
[...]
Post by David Webber
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
[...]
Post by David Webber
I am being told that the memory leak is associated with the CMyThing's
copy constructor invoked by push_back() above.
One more thing: reading the above code, it seems that this is not the case,
but maybe you posted a simplification of real code. So: I think that you
should also pay attention about *where* (i.e. which module) you
allocate/free data.
i.e. I think that if you allocate something in the heap from module X (e.g.
a given DLL), you should also free that in the *same* module X.
I'm not sure, but I think it is not safe to allocate something in the heap
from module X, and free in module Y.

Giovanni
David Webber
2007-10-10 22:46:23 UTC
Permalink
This post might be inappropriate. Click to display it.
Doug Harrison [MVP]
2007-10-11 17:26:53 UTC
Permalink
On Thu, 11 Oct 2007 00:32:05 +0200, "Giovanni Dicanio"
Post by Giovanni Dicanio
I'm not sure, but I think it is not safe to allocate something in the heap
from module X, and free in module Y.
It's fine if both modules link to the same CRT DLL. Given that David is
sharing C++ classes between modules, it's pretty much required.
--
Doug Harrison
Visual C++ MVP
Norbert Unterberg
2007-10-10 22:05:28 UTC
Permalink
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
CMyCollection : public std::vector<CMyThing>;
Its normal constructor is called on startup, which just creates and
empty collection.
It is only called once.
It is filled in (also once) with a method of CMyColllection
schematically of the form
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
It is usually not changed, and lasts until the program shuts down.
It is never copied. (It has a copy constructor, because I feel nervous
without them, but it is never called.)
The destructor is called once, and just calls clear() (because I feel
it is neat to do so).
I am being told that the memory leak is associated with the CMyThing's
copy constructor invoked by push_back() above.
How can that be?
If the memory leak is associated with CMyThing's copy constructor then I guess
it contains some deep copy opearions? If you overrode the CMyThing's copy
constructor, did you also override it's assignment operator and destructor? See
the "Law of the Big Three".
http://www.artima.com/cppsource/bigtwo.html

Norbert
David Webber
2007-10-10 22:37:16 UTC
Permalink
This post might be inappropriate. Click to display it.
David Wilkinson
2007-10-10 23:25:04 UTC
Permalink
Post by David Webber
Thank you! I have never needed that before. I now know where the
leak is, but not why.
In my non-MFC DLL I have a global instance of an object of the form
CMyCollection : public std::vector<CMyThing>;
Its normal constructor is called on startup, which just creates and
empty collection.
It is only called once.
It is filled in (also once) with a method of CMyColllection
schematically of the form
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
It is usually not changed, and lasts until the program shuts down.
It is never copied. (It has a copy constructor, because I feel nervous
without them, but it is never called.)
The destructor is called once, and just calls clear() (because I feel
it is neat to do so).
I am being told that the memory leak is associated with the CMyThing's
copy constructor invoked by push_back() above.
How can that be?
Even stranger, I have had all of this present in the code for years and
a memory leak has only been reported recently.
Any ideas gratefully received.
Dace:

What happens if you call clear() on your vector from somewhere else
(like ExitInstance()) rather than waiting for your derived destructor to
do it.

Actually, I don't think calling clear() from the destructor is
necessary, because the destructors of the contained objects are
automatically called by the std::vector destructor. I think the problem
is that your derived constructor is getting called too late relative to
the leak reporting mechanism.
--
David Wilkinson
Visual C++ MVP
David Ching
2007-10-10 23:33:17 UTC
Permalink
What happens if you call clear() on your vector from somewhere else (like
ExitInstance()) rather than waiting for your derived destructor to do it.
Actually, I don't think calling clear() from the destructor is necessary,
because the destructors of the contained objects are automatically called
by the std::vector destructor. I think the problem is that your derived
constructor is getting called too late relative to the leak reporting
mechanism.
That's what I thought also, but then why would it only start happening
recently when nothing has changed?

-- David
David Webber
2007-10-11 07:30:02 UTC
Permalink
Post by David Ching
What happens if you call clear() on your vector from somewhere else (like
ExitInstance()) rather than waiting for your derived destructor to do it.
I do that anyway - which is another puzzle.
Post by David Ching
Actually, I don't think calling clear() from the destructor is necessary,
because the destructors of the contained objects are automatically called
by the std::vector destructor. I think the problem is that your derived
constructor is getting called too late relative to the leak reporting
mechanism.
That's what I thought also, but then why would it only start happening
recently when nothing has changed?
That is a puzzle too - unless I have accidentally changed a setting in the
project which causes it to be reported. I don't *think* I have.

It isn't very serious. It is a bit of memory which should be used unchanged
(mostly) for the duration of the program, so having the system free it on
exit, isn't a disaster. But it's sloppy and I just *hate* it!

[The only time the memory is changed is when someone changes language
options: the instruments are unloaded and reloaded with their names in the
new language.]

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
David Wilkinson
2007-10-11 12:06:18 UTC
Permalink
Post by David Webber
Post by David Wilkinson
What happens if you call clear() on your vector from somewhere else
(like ExitInstance()) rather than waiting for your derived destructor
to do it.
I do that anyway - which is another puzzle.
In that case, it seems to me that the leak must be the memory allocated
for std::vector itself, rather than any memory internal to your CMyThing
objects.

What happens if you also shrink the vector to zero capacity (using the
swap trick)?
--
David Wilkinson
Visual C++ MVP
David Webber
2007-10-11 13:01:05 UTC
Permalink
Post by David Wilkinson
In that case, it seems to me that the leak must be the memory allocated
for std::vector itself, rather than any memory internal to your CMyThing
objects.
I am rather concerned that I might know why. Well -ish. As I have an MFC
program with no main(), I put the _CrtSetBreakAlloc in the CWinApp drived
class constructor.

If the global vector object in the DLL was being constructed before the
CWinApp was constructed, it couldn't have actually stopped the program on
the vector's constuctor. The fact that it stopped when something was added
to the vector may have been a complete coincidence - in which case I've had
a monumental bit of luck :-(

So where does one put _CrtSetBreakAlloc to guarantee it is invoked before
absolutely anything is constructed?

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
David Wilkinson
2007-10-11 13:36:38 UTC
Permalink
Post by David Webber
Post by David Wilkinson
In that case, it seems to me that the leak must be the memory
allocated for std::vector itself, rather than any memory internal to
your CMyThing objects.
I am rather concerned that I might know why. Well -ish. As I have an
MFC program with no main(), I put the _CrtSetBreakAlloc in the CWinApp
drived class constructor.
If the global vector object in the DLL was being constructed before the
CWinApp was constructed, it couldn't have actually stopped the program
on the vector's constuctor. The fact that it stopped when something
was added to the vector may have been a complete coincidence - in which
case I've had a monumental bit of luck :-(
So where does one put _CrtSetBreakAlloc to guarantee it is invoked
before absolutely anything is constructed?
Dave:

Constructing an empty vector does not assign any memory. When you add
things to it, the vector assigns memory and there may be additional
memory owned by the objects being placed in the vector.

I am not an expert on _CrtSetBreakAlloc(). I have always just used the
MFC DEBUG_NEW and have always been able to get rid of any leaks it
reported. But I don't use global objects.
--
David Wilkinson
Visual C++ MVP
David Ching
2007-10-11 13:40:22 UTC
Permalink
Post by David Wilkinson
But I don't use global objects.
Your CWinApp derived class is a global object! :-)

-- David
Giovanni Dicanio
2007-10-11 14:56:00 UTC
Permalink
Post by David Ching
Post by David Wilkinson
But I don't use global objects.
Your CWinApp derived class is a global object! :-)
:)

Yes, I was thinking about app class, too.

Maybe, in an elegant object-oriented designed system, the app instance
should be the only global object, and every other object should be created
by the app.

Giovanni
David Wilkinson
2007-10-11 17:25:03 UTC
Permalink
Post by David Ching
Post by David Wilkinson
But I don't use global objects.
Your CWinApp derived class is a global object! :-)
-- David
David:

Yes, but I don't think its constructor assigns any memory.
--
David Wilkinson
Visual C++ MVP
David Webber
2007-10-11 14:22:49 UTC
Permalink
Post by David Wilkinson
Constructing an empty vector does not assign any memory.
I'm not sure in what sense you mean this. Obviously if I put

CMyVector v;
Then &v will tell me where it is and sizeof v will tell me how big it is.
Ok, it is not being assigned dynamically but as a Global variable it sits
there and does not go out of scope until the program ends. In fact I have
always been rather vague on at what point its memory will be freed up,
because I have never really needed to know - until now :-)
Post by David Wilkinson
When you add things to it, the vector assigns memory and there may be
additional memory owned by the objects being placed in the vector.
But each will have its own block of assigned memory and I only got 1 leak
with 200 odd objects.
Post by David Wilkinson
I am not an expert on _CrtSetBreakAlloc(). I have always just used the MFC
DEBUG_NEW and have always been able to get rid of any leaks it reported.
But I don't use global objects.
I too am a believer in DEBUG_NEW but this was happening in a non-MFC DLL.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
David Wilkinson
2007-10-11 17:37:04 UTC
Permalink
Post by David Webber
Post by David Wilkinson
Constructing an empty vector does not assign any memory.
I'm not sure in what sense you mean this. Obviously if I put
CMyVector v;
Then &v will tell me where it is and sizeof v will tell me how big it
is. Ok, it is not being assigned dynamically but as a Global variable it
sits there and does not go out of scope until the program ends. In
fact I have always been rather vague on at what point its memory will be
freed up, because I have never really needed to know - until now :-)
Post by David Wilkinson
When you add things to it, the vector assigns memory and there may be
additional memory owned by the objects being placed in the vector.
But each will have its own block of assigned memory and I only got 1
leak with 200 odd objects.
Dave:

But only heap memory contributes to memory leaks, yes? If the objects
you put in your vector do not themselves assign heap memory (i.e. use
new internally) they will not contribute to memory leaks. The only
possible leak will come from the memory assigned by the vector itself.
And this should be released by the destructor, because std::vector is a
well-behaved RAII class.

I think the problem is just that the destructor of your global object is
getting called too late (and Doug thinks so also, so this must be right...).
--
David Wilkinson
Visual C++ MVP
David Webber
2007-10-11 18:09:52 UTC
Permalink
But only heap memory contributes to memory leaks, yes?...
I wasn't sure of that.
I think the problem is just that the destructor of your global object is
getting called too late (and Doug thinks so also, so this must be right...).
Yes, it makes sense.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
David Ching
2007-10-11 13:39:28 UTC
Permalink
Post by David Webber
Post by David Wilkinson
In that case, it seems to me that the leak must be the memory allocated
for std::vector itself, rather than any memory internal to your CMyThing
objects.
I am rather concerned that I might know why. Well -ish. As I have an
MFC program with no main(), I put the _CrtSetBreakAlloc in the CWinApp
drived class constructor.
If the global vector object in the DLL was being constructed before the
CWinApp was constructed, it couldn't have actually stopped the program on
the vector's constuctor. The fact that it stopped when something was
added to the vector may have been a complete coincidence - in which case
I've had a monumental bit of luck :-(
So where does one put _CrtSetBreakAlloc to guarantee it is invoked before
absolutely anything is constructed?
There is another way.
http://msdn2.microsoft.com/en-us/library/w2fhc9a3(VS.80).aspx describes
setting the _crtBreakAlloc variable to the desired value instead of calling
_CrtSetBreakAlloc(). So just step into your code in the debugger so it
stops at the entrypoint, then set this variable in the watch window.

OTOH, I'm not sure if it's too late once you step into your code... global
vars might have already been init. But you can try it.

BTW, I'm not sure why it's any concern because this is not a true memory
leak in that the memory is freed when the app is exited anyway. I
understand you don't like to see the mem leak output though, since it makes
it easy to ignore true memory leak output.

-- David
Doug Harrison [MVP]
2007-10-11 02:32:07 UTC
Permalink
On Wed, 10 Oct 2007 21:37:33 +0100, "David Webber"
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
That is the key sentence.
Post by David Webber
CMyCollection : public std::vector<CMyThing>;
Its normal constructor is called on startup, which just creates and empty
collection.
It is only called once.
It is filled in (also once) with a method of CMyColllection schematically of
the form
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
It is usually not changed, and lasts until the program shuts down.
It is never copied. (It has a copy constructor, because I feel nervous
without them, but it is never called.)
The destructor is called once, and just calls clear() (because I feel it
is neat to do so).
I am being told that the memory leak is associated with the CMyThing's copy
constructor invoked by push_back() above.
How can that be?
Even stranger, I have had all of this present in the code for years and a
memory leak has only been reported recently.
For some reason, your non-MFC DLL is loading before the MFC DLL now, and
during program termination, MFC is calling its "dump_spurious_leaks()"
function before your DLL has gotten the chance to destroy its globals.
Post by David Webber
Any ideas gratefully received.
See these messages for an explanation and workaround:

http://groups.google.com/group/microsoft.public.vc.mfc/msg/990674e598690af9
http://groups.google.com/group/microsoft.public.vc.language/msg/6b90e68f21529e56
--
Doug Harrison
Visual C++ MVP
David Webber
2007-10-11 07:43:33 UTC
Permalink
Post by Doug Harrison [MVP]
On Wed, 10 Oct 2007 21:37:33 +0100, "David Webber"
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
That is the key sentence.
...
http://groups.google.com/group/microsoft.public.vc.mfc/msg/990674e598690af9
http://groups.google.com/group/microsoft.public.vc.language/msg/6b90e68f21529e56
Thanks Doug,

That appears to explain just about everything. And it makes me very
happy, because it means I did not program a memory leak - and thus delays
the fall I expect following my pride in never doing so :-)

I try to avoid global objects like the plague and this was my only infection
in this DLL. Most of my stuff which really needs to be global is stored
as a static pointer in a class, and the object is created by my InitInstance
code and explicitly deleted on ExitInstance. Once Norbert had told me how
to find it, the coincidence of global object and (apparent) memory leak
started ringing alarm bells.

I am now restructuring to handle this object like the others - which should
cure it as the memory will be freed from a call made by the MFC module.

Onward and upward.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
David Webber
2007-10-11 09:24:22 UTC
Permalink
Post by Doug Harrison [MVP]
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
That is the key sentence.
...
All fixed now - thanks again to everyone.

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Giovanni Dicanio
2007-10-11 09:40:20 UTC
Permalink
Post by David Webber
Post by Doug Harrison [MVP]
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
That is the key sentence.
...
All fixed now - thanks again to everyone.
Thanks to you because we (or at least, I) learnt important lessons from your
post, like how super-important is to avoid global variables.

Giovanni
David Webber
2007-10-11 13:02:43 UTC
Permalink
Post by Giovanni Dicanio
Thanks to you because we (or at least, I) learnt important lessons from
your post, like how super-important is to avoid global variables.
Yes I have told myself that many times. It is my punishment for not taking
enough notice :-)

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Giovanni Dicanio
2007-10-11 13:14:56 UTC
Permalink
Post by David Webber
[...] how super-important is to avoid global variables.
Yes I have told myself that many times. It is my punishment for not
taking enough notice :-)
:)

Gio
Doug Harrison [MVP]
2007-10-11 17:26:53 UTC
Permalink
On Thu, 11 Oct 2007 11:40:20 +0200, "Giovanni Dicanio"
Post by Giovanni Dicanio
Post by David Webber
Post by Doug Harrison [MVP]
Post by David Webber
In my non-MFC DLL I have a global instance of an object of the form
That is the key sentence.
...
All fixed now - thanks again to everyone.
Thanks to you because we (or at least, I) learnt important lessons from your
post, like how super-important is to avoid global variables.
I hope there is a better reason than to avoid spurious leak reporting by
MFC. :) It's not just "globals"; this problem affects any object that is
destroyed after MFC calls its "cause_spurious_leak_report()" function. For
example, MFC would have caused the same problem if the OP's vector had been
a static data member of one of the classes in his DLL.

When I said "That is the key sentence," the implied second part was,
"...that helps me identify the problem." I did not mean that having a
global object was the problem, just that "non-MFC DLL", "global object",
and mysterious "memory leaks" remind me of this long-standing MFC bug.
--
Doug Harrison
Visual C++ MVP
John Depth
2012-04-06 19:56:54 UTC
Permalink
I prefer use Deleaker for similar cases.
Post by David Webber
I have a small memory leak which is driving me mad!
I have done the usual DEBUG_NEW stuff in all the files of the MFC exe and
DLLs. that usually does the trick but this time it isn't telling me about
it.
The problem may be in a non-MFC DLL. Any suggestions for how to find it?
[Years ago I had Bounds Checker but it got to be too expensive upgrading it
every time a new version of Windows became current, but DEBUG_NEW has been
ok for me ever since.]
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by Norbert Unterberg
How do you know about the leak? Do you see the memory leak dump in the IDE's
output window when your application exits?
If yes then you have a good chance. You can set a memory breakpoint on the
allocation ID that is listed in the memory leaks dump. Add a call to
_CrtSetBreakAlloc(<put the ID here>); at the beginning of main()
Norbert
Post by David Webber
Yes.
Thank you! I have never needed that before. I now know where the leak
is, but not why.
In my non-MFC DLL I have a global instance of an object of the form
CMyCollection : public std::vector<CMyThing>;
Its normal constructor is called on startup, which just creates and empty
collection.
It is only called once.
It is filled in (also once) with a method of CMyColllection schematically of
the form
CMyCollection::FillIn()
{
CMyThing thing;
for( allthings )
{
thing = .... // fill in the thing
push_back( thing ); //*********
}
}
It is usually not changed, and lasts until the program shuts down.
It is never copied. (It has a copy constructor, because I feel nervous
without them, but it is never called.)
The destructor is called once, and just calls clear() (because I feel it
is neat to do so).
I am being told that the memory leak is associated with the CMyThing's copy
constructor invoked by push_back() above.
How can that be?
Even stranger, I have had all of this present in the code for years and a
memory leak has only been reported recently.
Any ideas gratefully received.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by Norbert Unterberg
If the memory leak is associated with CMyThing's copy constructor then I guess
it contains some deep copy opearions? If you overrode the CMyThing's copy
constructor, did you also override it's assignment operator and destructor? See
the "Law of the Big Three".
http://www.artima.com/cppsource/bigtwo.html
Norbert
Post by Giovanni Dicanio
Hi Dave,
[...]
Could you please try to use a Has-A "embedding" relationship (not Is-A via
inheritance), i.e. : putting a private/protected instance of
std::vector<CMyThing> into CMyCollection.
[...]
It would be interesting to see some code for CMyThing... (maybe it has got
some non-smart pointers that are not correctly copied, or it hasn't got a
virtual destructor that would be required...).
Also, you should pay attention about the C/C++ run-time used by each module
(DLLs and EXE).
I think it should be the same (e.g. debug multithreaded DLL, or
multithreaded DLL for release builds).
Maybe you are now using VS2005 which implements better checking for memory
leaks than VC6 or 2003...
Ciao
Giovanni
Post by Giovanni Dicanio
[...]
One more thing: reading the above code, it seems that this is not the case,
but maybe you posted a simplification of real code. So: I think that you
should also pay attention about *where* (i.e. which module) you
allocate/free data.
i.e. I think that if you allocate something in the heap from module X (e.g.
a given DLL), you should also free that in the *same* module X.
I'm not sure, but I think it is not safe to allocate something in the heap
from module X, and free in module Y.
Giovanni
Post by David Webber
Not particularly deep...
... and yes - I am particular to the point of pedantry about that sort of
thing.
But explaining the problem has given me an idea.
=========================
this warning.
// This seems to be necessary if the class derived from vector is to be
exported.
EXTERN_MZWN_API template class MZWN_API
std::allocator<CPercussionInstrument>;
EXTERN_MZWN_API template class MZWN_API std::vector<CPercussionInstrument>;
// class CPercussionSet
class MZWN_API CPercussionSet : public std::vector<CPercussionInstrument>
{
CPercussionSet();
CPercussionSet( const CPercussionSet &ps );
virtual ~CPercussionSet();
CPercussionSet & operator = ( const CPercussionSet &ps );
//....
};
=============
I am pretty sure the copy constructor, destructor, and assignment of
CPercussionInstrument are ok (I am checking). I *am* a little concerned
about exporting this class from the DLL, (in which the only instance is
global) and wondering if that is at the root of things. It may not be
being correctly destroyed when the DLL is unloaded - though the destructor
*is* getting called.
Perhaps I should store a global; pointer to a CPercussionSet and explicitly
create and destroy it with new and delete. That way I'd have more control
over when it happens! I'll experiment.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by David Webber
Thanks! This is the way I am starting to think too - see other post. With
the class being global in the DLL I am not in control of where it is
constructed and destroyed. I propose to do something about that tomorrow -
it's late now.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
.
Post by David Wilkinson
What happens if you call clear() on your vector from somewhere else
(like ExitInstance()) rather than waiting for your derived destructor to
do it.
Actually, I don't think calling clear() from the destructor is
necessary, because the destructors of the contained objects are
automatically called by the std::vector destructor. I think the problem
is that your derived constructor is getting called too late relative to
the leak reporting mechanism.
--
David Wilkinson
Visual C++ MVP
Post by David Ching
That's what I thought also, but then why would it only start happening
recently when nothing has changed?
-- David
Post by Doug Harrison [MVP]
On Wed, 10 Oct 2007 21:37:33 +0100, "David Webber"
That is the key sentence.
For some reason, your non-MFC DLL is loading before the MFC DLL now, and
during program termination, MFC is calling its "dump_spurious_leaks()"
function before your DLL has gotten the chance to destroy its globals.
http://groups.google.com/group/microsoft.public.vc.mfc/msg/990674e598690af9
http://groups.google.com/group/microsoft.public.vc.language/msg/6b90e68f21529e56
--
Doug Harrison
Visual C++ MVP
Post by David Webber
I do that anyway - which is another puzzle.
That is a puzzle too - unless I have accidentally changed a setting in the
project which causes it to be reported. I don't *think* I have.
It isn't very serious. It is a bit of memory which should be used unchanged
(mostly) for the duration of the program, so having the system free it on
exit, isn't a disaster. But it's sloppy and I just *hate* it!
[The only time the memory is changed is when someone changes language
options: the instruments are unloaded and reloaded with their names in the
new language.]
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by David Webber
Thanks Doug,
That appears to explain just about everything. And it makes me very
happy, because it means I did not program a memory leak - and thus delays
the fall I expect following my pride in never doing so :-)
I try to avoid global objects like the plague and this was my only infection
in this DLL. Most of my stuff which really needs to be global is stored
as a static pointer in a class, and the object is created by my InitInstance
code and explicitly deleted on ExitInstance. Once Norbert had told me how
to find it, the coincidence of global object and (apparent) memory leak
started ringing alarm bells.
I am now restructuring to handle this object like the others - which should
cure it as the memory will be freed from a call made by the MFC module.
Onward and upward.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by David Webber
All fixed now - thanks again to everyone.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by Giovanni Dicanio
Thanks to you because we (or at least, I) learnt important lessons from your
post, like how super-important is to avoid global variables.
Giovanni
Post by David Wilkinson
In that case, it seems to me that the leak must be the memory allocated
for std::vector itself, rather than any memory internal to your CMyThing
objects.
What happens if you also shrink the vector to zero capacity (using the
swap trick)?
--
David Wilkinson
Visual C++ MVP
Post by David Webber
I am rather concerned that I might know why. Well -ish. As I have an MFC
program with no main(), I put the _CrtSetBreakAlloc in the CWinApp drived
class constructor.
If the global vector object in the DLL was being constructed before the
CWinApp was constructed, it couldn't have actually stopped the program on
the vector's constuctor. The fact that it stopped when something was added
to the vector may have been a complete coincidence - in which case I've had
a monumental bit of luck :-(
So where does one put _CrtSetBreakAlloc to guarantee it is invoked before
absolutely anything is constructed?
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by David Webber
Yes I have told myself that many times. It is my punishment for not taking
enough notice :-)
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Gio
Post by David Wilkinson
Constructing an empty vector does not assign any memory. When you add
things to it, the vector assigns memory and there may be additional
memory owned by the objects being placed in the vector.
I am not an expert on _CrtSetBreakAlloc(). I have always just used the
MFC DEBUG_NEW and have always been able to get rid of any leaks it
reported. But I don't use global objects.
--
David Wilkinson
Visual C++ MVP
Post by David Ching
There is another way.
http://msdn2.microsoft.com/en-us/library/w2fhc9a3(VS.80).aspx describes
setting the _crtBreakAlloc variable to the desired value instead of calling
_CrtSetBreakAlloc(). So just step into your code in the debugger so it
stops at the entrypoint, then set this variable in the watch window.
OTOH, I'm not sure if it's too late once you step into your code... global
vars might have already been init. But you can try it.
BTW, I'm not sure why it's any concern because this is not a true memory
leak in that the memory is freed when the app is exited anyway. I
understand you don't like to see the mem leak output though, since it makes
it easy to ignore true memory leak output.
-- David
Post by David Ching
Your CWinApp derived class is a global object! :-)
-- David
Post by David Webber
I'm not sure in what sense you mean this. Obviously if I put
CMyVector v;
Then &v will tell me where it is and sizeof v will tell me how big it is.
Ok, it is not being assigned dynamically but as a Global variable it sits
there and does not go out of scope until the program ends. In fact I have
always been rather vague on at what point its memory will be freed up,
because I have never really needed to know - until now :-)
But each will have its own block of assigned memory and I only got 1 leak
with 200 odd objects.
I too am a believer in DEBUG_NEW but this was happening in a non-MFC DLL.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Post by Giovanni Dicanio
Yes, I was thinking about app class, too.
Maybe, in an elegant object-oriented designed system, the app instance
should be the only global object, and every other object should be created
by the app.
Giovanni
Yes, but I do not think its constructor assigns any memory.
--
David Wilkinson
Visual C++ MVP
it is fine if both modules link to the same CRT DLL. Given that David is
sharing C++ classes between modules, it is pretty much required.
--
Doug Harrison
Visual C++ MVP
Post by Doug Harrison [MVP]
On Thu, 11 Oct 2007 11:40:20 +0200, "Giovanni Dicanio"
I hope there is a better reason than to avoid spurious leak reporting by
MFC. :) It's not just "globals"; this problem affects any object that is
destroyed after MFC calls its "cause_spurious_leak_report()" function. For
example, MFC would have caused the same problem if the OP's vector had been
a static data member of one of the classes in his DLL.
When I said "That is the key sentence," the implied second part was,
"...that helps me identify the problem." I did not mean that having a
global object was the problem, just that "non-MFC DLL", "global object",
and mysterious "memory leaks" remind me of this long-standing MFC bug.
--
Doug Harrison
Visual C++ MVP
Post by David Wilkinson
But only heap memory contributes to memory leaks, yes? If the objects
you put in your vector do not themselves assign heap memory (i.e. use
new internally) they will not contribute to memory leaks. The only
possible leak will come from the memory assigned by the vector itself.
And this should be released by the destructor, because std::vector is a
well-behaved RAII class.
I think the problem is just that the destructor of your global object is
getting called too late (and Doug thinks so also, so this must be right...).
--
David Wilkinson
Visual C++ MVP
Post by David Webber
I wasn't sure of that.
Yes, it makes sense.
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mzusers/mailinglist.htm
Continue reading on narkive:
Loading...