Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


Memory Leaks


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
11 replies to this topic

#1 Vanz   Members   -  Reputation: 204

Like
0Likes
Like

Posted 12 February 2013 - 08:41 PM

I always strive to write my programs so they are Rock Solid, please share what techniques and/or programs you use to hunt down those nasty memory leaks. Some common ones I run into

int i, a[10];

for(i=0;i<15;i++)
    a[i]=i;


One technique I use to track them down or errors in general is I write a for loop to a high number say 100,000 then I keep calling that function. If it has a memory leak it will usually crash or I examine Task Manager-> Performance and watch the Free Memory shrink.

Another one, that I think causes a crash:


BitBlt(hdc, -5, -5 , 100 , 100, hdcPic, 0, 0, SRCCOPY);


When using GDI, does blitting outside the screen resolution cause a memory leak?

 

Any other common ones you can think of? An example is always helpful... how do you find em?

Thanks for sharing...

Vanz



Sponsor:

#2 ProvenDantheman   Members   -  Reputation: 111

Like
0Likes
Like

Posted 12 February 2013 - 08:51 PM

Well, I always make sure my dynamic arrays are deleted before allocating more memory, and I also make sure I allocate enough memory before accessing it.

 

Don't do this:

 

int * i = new int[10];

 

somefunc(){

i = new int[11];

}

 

instead do this:

 

int * i = new int[10];

 

somefunc(){

delete[]i;

i = new int[11];

}

 

Do not do this:

 

int * i = new int[10];

 

somefunc{

int var = i[11];

}

 

Make sure you allocated enough memory in your dynamic array.

 

Also, make sure you only call delete once, or if there is a possible instance of you calling delete twice, set the variable to NULL when you delete it. Deleting a NULL pointer has no effect.


Edited by ProvenDantheman, 12 February 2013 - 08:54 PM.


#3 Khatharr   Crossbones+   -  Reputation: 3056

Like
2Likes
Like

Posted 12 February 2013 - 09:32 PM

rhuala, on 12 Feb 2013 - 18:49, said:

int i, a[10];
for(i=0;i&lt;15;i++)
  a[i]=i;

That's not a memory leak. That's a buffer overrun. You can avoid it by not using magic numbers:
const size_t ARRAY_LENGTH = 10; //or whatever
int ary[ARRAY_LENGTH];
for(int i=0; i &lt; ARRAY_LENGTH; ++i) {
   ary[i] = i;
}

Quote
One technique I use to track them down or errors in general is I write a for loop to a high number say 100,000 then I keep calling that function. If it has a memory leak it will usually crash or I examine Task Manager-&gt; Performance and watch the Free Memory shrink.

No bueno. This will only catch leaks that always happen, and it's too hard to distinguish from normal allocation behavior if leaks are small. Use a utility like VLD instead, and use smart pointers and RAII to make leakage a non-issue.

Quote
Another one, that I think causes a crash:

BitBlt(hdc, -5, -5 , 100 , 100, hdcPic, 0, 0, SRCCOPY);
When using GDI, does blitting outside the screen resolution cause a memory leak?

No. It clips the blit according to the target context.

You're confused about what a memory leak is, or at least about what causes them.

This is a memory leak:
int main() {
  int* derp = new int;
  return 0;
}

This is a harmless one, but it's a leak because memory is allocated and not freed. You avoid memory leaks by strictly following proper memory management practices like RAII.

The examples you gave were concerning buffer overrun behavior. You avoid overruns by limiting buffer actions by always implementing bounds checking. Write loops in such a way that they are made aware of - and are unable to pass beyond - the end of the buffer, etc. Use the secure CRT functions rather than the original ones.

ProvenDantheman, on 12 Feb 2013 - 18:59, said:
Also, make sure you only call delete once, or if there is a possible instance of you calling delete twice, set the variable to NULL when you delete it. Deleting a NULL pointer has no effect.

Obscuring a bug is not the same as fixing a bug. Your program should always be aware of its allocations and react intelligently. NULL deletion has its uses but abusing it to avoid implementing allocation control is like taking aspirin instead of going to the hospital after cutting your arm off.

Edited by Khatharr, 12 February 2013 - 09:53 PM.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#4 Servant of the Lord   Crossbones+   -  Reputation: 21164

Like
0Likes
Like

Posted 12 February 2013 - 10:09 PM

I avoid dynamic memory except where performance is really needed, preferring the priority of: local or member variables -> smart pointers -> manual dynamic memory
I use std::vectors even when I need a static size (I'll switch to C++11's std::array for static sizes soon - last I checked that wasn't implemented in my compiler, but that was quite a while ago).
Other than that, I also avoid funky pointer manipulation, and in general prefer references where suitable.

(The latter two helping with memory corruption rather than memory leaks - something that I dread trying to debug)


Edited by Servant of the Lord, 12 February 2013 - 10:10 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#5 Hodgman   Moderators   -  Reputation: 31926

Like
0Likes
Like

Posted 12 February 2013 - 10:15 PM


rhuala, on 12 Feb 2013 - 18:49, said:

int i, a[10];
for(i=0;i&lt;15;i++)
  a[i]=i;


That's not a memory leak. That's a buffer overrun. You can avoid it by not using magic numbers:
const size_t ARRAY_LENGTH = 10; //or whatever
int ary[ARRAY_LENGTH];
for(int i=0; i < ARRAY_LENGTH; ++i) {
   ary[i] = i;
}


QFT - the OP's examples are all about memory corruption, not memory leaks at all sad.png

Another technique that you can use for arrays (but not arrays that have been cast/decayed into pointers) is:
template <typename T, int N> uint ArraySize(T(&)[N]) { return N; }
...
for(int i=0; i < ArraySize(ary); ++i) {
   ary[i] = i;
}


#6 Polarist   Members   -  Reputation: 160

Like
1Likes
Like

Posted 12 February 2013 - 10:53 PM

It may be not as efficient, but I tend to use STL everywhere (until I need to optimize).  It's much easier to maintain/read.

const int NUM_THINGS = 10;

std::vector<int> myThings(NUM_THINGS);

for( const auto& thing : myThings ){
   do_something_with(thing);
}

It also makes it quite difficult to accidentally overrun your buffer.


Edited by Polarist, 12 February 2013 - 10:54 PM.


#7 Vanz   Members   -  Reputation: 204

Like
0Likes
Like

Posted 13 February 2013 - 01:16 AM

 

rhuala, on 12 Feb 2013 - 18:49, said:



int i, a[10];
for(i=0;i&lt;15;i++)
  a[i]=i;

 

That's not a memory leak. That's a buffer overrun. You can avoid it by not using magic numbers:


const size_t ARRAY_LENGTH = 10; //or whatever
int ary[ARRAY_LENGTH];
for(int i=0; i < ARRAY_LENGTH; ++i) {
   ary[i] = i;
}

 

QFT - the OP's examples are all about memory corruption, not memory leaks at all sad.png

Another technique that you can use for arrays (but not arrays that have been cast/decayed into pointers) is:


template <typename T, int N> uint ArraySize(T(&)[N]) { return N; }
...
for(int i=0; i < ArraySize(ary); ++i) {
   ary[i] = i;
}

okay my bad, I apologize... whatever it's called it leads to crashes or flaky behavior, I guess my technique sucks... although I never do allocate an array with a number, it's always a #define or const


Edited by rhuala, 13 February 2013 - 01:19 AM.


#8 nfactorial   Members   -  Reputation: 731

Like
0Likes
Like

Posted 13 February 2013 - 02:28 AM

I just replace operator new and delete (and malloc and free) with my own memory manager. You can then inject data blocks at the front and back of the allocated memory and place a defined data block there. You can detect buffer overruns by validating they are unchanged (the typical term for this is 'sentinels'). You can also detect memory leaks easily, most of that functionality is removed in release or master builds for performance reasons, and it can becoime a fairly large module in itself depending how much functionality you want but it's the best way to track downn these issues that can occur in all levels of C++ programming.

 

n!


Edited by nfactorial, 13 February 2013 - 02:28 AM.


#9 Khatharr   Crossbones+   -  Reputation: 3056

Like
0Likes
Like

Posted 13 February 2013 - 02:48 AM

rhuala, on 12 Feb 2013 - 23:24, said:
okay my bad, I apologize... whatever it's called it leads to crashes or flaky behavior, I guess my technique sucks... although I never do allocate an array with a number, it's always a #define or const

Not yelling at you. Just clarifying the difference between the concepts.
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

#10 Felix Ungman   Members   -  Reputation: 1066

Like
0Likes
Like

Posted 13 February 2013 - 03:37 AM

please share what techniques and/or programs you use to hunt down those nasty memory leaks.

 

During development I try to use good coding practices like RAII when appropriate and not separating allocation/deallocation responsibility.

 

Then when there's time for a release I do a profiling session using the Instruments app that comes with Xcode. It's very good for tracking memory allocation and leaks, but sometimes I also turn on memory scribble and guards to do even more thorough testing.


openwar  - the real-time tactical war-game platform


#11 Bacterius   Crossbones+   -  Reputation: 9294

Like
2Likes
Like

Posted 13 February 2013 - 04:31 AM

I typically just run valgrind on my program, it catches basically everything. Sometimes it's helpful enough to tell me exactly what is leaking, though generally it's easy enough to deduce that from the size of the leaked resource. Though that's under Linux, and I'm not sure if opengl and other large libraries work together well with valgrind, but never had a problem so far.

 

I usually don't worry too much about memory leaks anyway. If your program is leaking memory, you may just have forgot something somewhere and that's easy to fix. If your design is so bad that it is otherwise incapable of correctly freeing its memory, you have big architectural problems. So usually, I just run valgrind from time to time, to pick up any accidental leaks I might have missed, and I'm good to go. That's in C, anyway. In C++ it's almost impossible to leak memory with the RAII model.


Edited by Bacterius, 13 February 2013 - 04:32 AM.

The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#12 Polarist   Members   -  Reputation: 160

Like
0Likes
Like

Posted 13 February 2013 - 04:48 AM

I just replace operator new and delete (and malloc and free) with my own memory manager. You can then inject data blocks at the front and back of the allocated memory and place a defined data block there. You can detect buffer overruns by validating they are unchanged (the typical term for this is 'sentinels'). You can also detect memory leaks easily, most of that functionality is removed in release or master builds for performance reasons, and it can becoime a fairly large module in itself depending how much functionality you want but it's the best way to track downn these issues that can occur in all levels of C++ programming.

 

n!

I'm very interested in this approach, could you share any resources that you find particularly useful for implementing this approach?  It seems like a very elegant way to approach this issue, and would be much cleaner syntax than what I'm used to doing (i.e. shenanigans in constructors or a clunky factory approach).






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS