Sign in to follow this  
shurcool

Would this produce a memory leak?

Recommended Posts

I just ran into something I never realized was a problem:
#include <stdlib.h>
#include <list>

int main()
{
    list<int> list1(100, 123); // Create a list with 100 ints with value 123.

    exit(0); // The list ~deconstructor is not called, creating a memory leak!
}

I know that list1 would call new 100 times for each of the ints, and if called, its ~destructor would clean that up. However, since it's on the stack, and I call exit(0) rather than return 0, is its ~destructor even called? According to a simple test I did with my own class, it's not! According to exit() description, it's supposed to "Terminate the process normally, performing the regular cleanup for terminating processes." That doesn't seem to be a normal clean up to me, as it creates memory leaks. Is there any way of using exit() to exit without such side-effects, or is my only way to be sure everything is cleaned up is to call return 0; from main()? Thanks in advance.

Share this post


Link to post
Share on other sites
exit() will perform normal C cleanup, calling atexit() handlers, etc. However, it generally won't perform stack unwind cleanup for C++ objects on the stack.

Share this post


Link to post
Share on other sites
Right.

So my question then is: Is there any safe way to use exit() to exit from within the middle of an application without potentially leaving memory allocated on the stack behind (i.e. creating a memory leak)?

Dynamically allocated memory is not a problem, as I can clean it up myself just before exiting.

If not, what's a good method to self-terminate a program? Is returning from main() the only safe way?

Share this post


Link to post
Share on other sites
Basically, returning from main() is the only way that C++ will guarantee that everything will shut down properly. Specific C++ implementations may have other mechanisms. For example, most C++ implementations will clean up properly if you have an unhandled exception, but it's not guaranteed to work.

Share this post


Link to post
Share on other sites
Okay, I'll adapt my code to have Terminate() set some condition variable like 'running' to false, instead of calling exit() explicitly. That will make the program fall out of the main loop, and eventually get to the bottom of main().

That's probably not the best way though...

Thanks for the help.

Share this post


Link to post
Share on other sites
I don't see the problem if the program is going to exit anyway. To be a leak, the program should still be running.

Share this post


Link to post
Share on other sites
I thought a memory leak was when you exit without deallocating all the memory that you've allocated (usually memory dynamically allocated on the heap, but I don't see why memory allocated on the stack wouldn't count).

Now you've made me unsure, lol. Does an OS have some magical way to restore (i.e. make avaliable for future use; mark as 'free memory') all the memory allocated to a program when it exists, even if that program itself doesn't clean up after itself? I think not... right? But then again, if a program always allocates memory for itself within its own memory space, it *might* be possible...

So what's the scoop on this?

Edit: Wow, apparently that might be true, about the OS forcebly cleaning up after a program terminates. According to wikipedia, anyway:

"In modern operating systems, normal memory used by an application is released when the application terminates. This means that a memory leak in a program that only runs for a short time is rarely serious."

This is starting to make sense along with what I've learned in my OS class. I can't believe I never realized this was the case, until now. Well, I could use more assurance from other people to be completely sure...

Does that make it safe to exit() while leaving allocated memory behind?

Wow, I can't believe how one's world, once thought to be solid and stable for so long, can be turned upside down by a simple finding like this lol. It goes against everything I've learned and thought about so far...

Share this post


Link to post
Share on other sites
It's a memory leak imo. The application doesn't deconstruct properly. Beyond the obvious issues with stuff like memory, what about other handles the process has open? There's many things you can leak and different operating systems will behave differently when trying to clean up the process after exit. Some embedded systems might not clean it at all, older versions of Windows didn't either. Making it exit properly here is trivial and you're aware of the issue so there's no reason except laziness to not fix it imo.

Share this post


Link to post
Share on other sites
Quote:
Original post by owl
I don't see the problem if the program is going to exit anyway. To be a leak, the program should still be running.


Even if Windows does clean up your memory and handles for you, I can't imagine how leaving them around is good programming practice for C and C++. Plus it's not hard to imagine a class that causes trouble if it's destructor never gets called (especially when multiple threads and DLL's are involved).

Share this post


Link to post
Share on other sites
Ok, so not all hope is lost then - at least it's still a good programming practice not to leave memory leaks behind. :) Maybe I'm not that useless after all then. :D

Thanks.

Still, I can't believe I never realized that Windows XP does clean up after programs that do create memory leaks while running. Now that I think about it, it seems so obvious (I can think of many examples in the past that confirm this behaviour). This probably wasn't the case in Win 98, was it?

Share this post


Link to post
Share on other sites
Quote:
Original post by owl
I don't see the problem if the program is going to exit anyway. To be a leak, the program should still be running.


I agree. Although, it is a good practice for several reasons to clean up before we exit, rather than hoping the OS will do the job for us (some reasons: OS bugs, forgetting to delete critical buffers may imply we're also forgetting to save it's content to disk, etc)

How about this?:

[source=cpp]
void MyExit( list<int> &l )
{
l.clear();
exit(0);
}

int main()
{

list<int> list1(100, 123); // Create a list with 100 ints with value 123.

MyExit( list1 );
}



Not the finest solution, but it's something. Why do you want to use/need exit() anyway?
I haven't ever used it in my development years. Try thinking in a better design that avoids using it.
And if you're planning to use it for emergencies (i.e. crashes) it is fine to cause memory leaks, as often crashes are some memory pointers are corrupted, invalid or overflowed, in which case you wouldn't be able to free anyway.
Also take note that this code:

[source=cpp]
void MyMain()
{
list<int> list1(100, 123); // Create a list with 100 ints with value 123.
}

int main()
{
MyMain();
exit(0);
}



will work since list1 is in MyMain's stack, not in main()

Hope this helps
Dark Sylinc

Share this post


Link to post
Share on other sites
It's not really "magical"...why wouldn't the OS know about memory that's been allocated for a process? The OS is what allocated it in the first place. Besides Windows would slow to a crawl if every bit of memory leaked became permanent.

This doesn't at all mean you should rely on this behavior. Different platforms can do whatever they want, and Windows is even technically free to change this behavior since it's not documented in ExitProcess (although in practice they never would, since I'm sure countless buggy apps do rely on it).

Share this post


Link to post
Share on other sites
Quote:
Original post by Matias Goldberg
How about this?:

*** Source Snippet Removed ***

Not the finest solution, but it's something. Why do you want to use/need exit() anyway?

Well, obviously I can't do that because it was just a simple example. In practice, I may call Terminate() from any place, so I don't know what is currently allocated on the stack.

My application is a game client and a dedicated server for it. In practice, the client always terminates by returning from main() when you exit normally. I only use Terminate(int nExitCode), which deallocates all (ok, it's actually 'most' rather than 'all' if it's called in an unstable place, like right after a 'new int;') memory on the heap and calls exit(nExitCode), when something abnormal happens, and I need to forcedly 'crash' basically.

However, since dedicated server doesn't have a GUI, the only way I found that I could get it to shut down nicely (along with its threads) is to create a Ctrl+C signal handler, and call Terminate there. So this was what worried me the most. Since it's the only way to terminate the server, and it may create leaks.

However, given what I've learned from this thread so far, it makes me feel better that it's ok if I don't always clean up every single byte allocated when the program is shut down in an unexpected way, or crashes.

As long as it does clean up everything on normal termination, I'm happy. I'll have to change the way 'normal termination' occurs on the server, though.

What mostly bothered me was that I didn't know all the details behind these processes, which I think is pretty important (I don't want to write crappy software with memory leaks all over the place). But it's good that I have a better understanding of it now, thanks to all your help. :)

Share this post


Link to post
Share on other sites
Having a 2nd thought, (but I'm not sure how accurate it can be) memory stack is the least of our problems, as if I recall correctly the OS assigns certain ammount of memory in the stack for each app. Once the app is finished, the whole stack freed. Unlike dinamyc allocation where memory points almost wherever in the RAM, the stack has a more "lineal" way of allocating things.
For example, the stack from my app goes from 0x000000aa to 0x000000FF (easy numbers, that memory region is forbidden for regular apps) So the first list will be placed at 0x000000aa the 2nd one at 0x000000AB and so on. We can use assembly to "skip" some slots, but we can't never exceed 0x000000FF. If we do stack overflow occurs. When the app exits 0x000000aa to 0x000000FF becomes free. We can always know that any variable in the stack will be inside that region.

It is a bit more complicated than this, and I may have made a mistake here since it's been a long time I don't do such low level programming.

Dark Sylinc

Share this post


Link to post
Share on other sites
Quote:
Original post by Matias Goldberg
Having a 2nd thought, (but I'm not sure how accurate it can be) memory stack is the least of our problems, as if I recall correctly the OS assigns certain ammount of memory in the stack for each app. Once the app is finished, the whole stack freed. Unlike dinamyc allocation where memory points almost wherever in the RAM, the stack has a more "lineal" way of allocating things.
For example, the stack from my app goes from 0x000000aa to 0x000000FF (easy numbers, that memory region is forbidden for regular apps) So the first list will be placed at 0x000000aa the 2nd one at 0x000000AB and so on. We can use assembly to "skip" some slots, but we can't never exceed 0x000000FF. If we do stack overflow occurs. When the app exits 0x000000aa to 0x000000FF becomes free. We can always know that any variable in the stack will be inside that region.

It is a bit more complicated than this, and I may have made a mistake here since it's been a long time I don't do such low level programming.

The concept of Virtual Memory makes that a non-issue for the OS (if the said OS uses such a memory management model). It makes no difference if the memory was allocated on the stack or heap, everything can be freed in one shot when the process ends.

Share this post


Link to post
Share on other sites
Quote:
Original post by shurcool
dedicated server doesn't have a GUI, the only way I found that I could get it to shut down is to create a Ctrl+C signal handler, and call Terminate there. So this was what worried me the most. Since it's the only way to terminate the server, and it may create leaks.

However, given what I've learned from this thread so far, it makes me feel better that it's ok if I don't always clean up every single byte allocated when the program is shut down in an unexpected way, or crashes.


OK, If I were you, in the main loop I would check for a boolean "request_terminate" or something, and check if it is true and then go out of the main loop. The Ctrl+C signal handler would set that variable to true. And if there is some sort of intensive calculation that may arise after Ctrl+C is pressed (since we check the boolean after the loop restarted and then exit) then check if that boolean is true before doing that intensive calculation and skip it. That is what I mean with "design change"

Although, I have to admit calling exit() is the cheapest and quickest solution. Furthermore, you're creating a server application. Those apps are supposed to run without interruption for hours, days, and even years. If you're debugging, you don't need to worry about leaks as you can restart the OS. If you're in the final release, Ctrl+C will barely need to be called. If you need Ctrl+C in the final release, then you're surely having server problems which more likely will requiere to reboot the system.

Share this post


Link to post
Share on other sites
Quote:
Original post by shurcool
The concept of Virtual Memory makes that a non-issue for the OS (if the said OS uses such a memory management model). It makes no difference if the memory was allocated on the stack or heap, everything can be freed in one shot when the process ends.


Thanks for pointing it out. I didn't know virtualization is used too for the stack.
Also reading that article reminds me that if you're making a server application, you have to be extremely carefull with memory leaks and even memory fragmentation.

Dark Sylinc

Share this post


Link to post
Share on other sites
Instead of using MyExit (see previous posts), why not put your cleanup code in a function and use "atexit(...)" to call that function when exit(0) is called? That way, you can still use exit(0).

Anyway, I don't like any of this - in fact, I wasn't even aware of the "exit" function (I just return from main and everything turns out fine). From what I understand, exit is a c function, and shouldn't be used when c++ classes are used.

Anyway, a memory leak to me is when a program doesn't clean itself up - whether the operating system does afterwards or not. If the OS does it automatically, that's nice - but one should never depend on that behavior.

Share this post


Link to post
Share on other sites

Instead of calling a specific function to do clean up and trying to code for, and always ensure you pass it the things you must clean up, you could alternatively just throw an exception. The specific exception which could be thrown from anywhere (ie inside a message pump handling function) would get caught in the main() block and should force the unwinding of the stack in turn releasing all resources that are managed via raii such as stl containers and shared pointer resources.

It doesnt seem a completely ideal solution or a philosophically appropriate use of exception handling but it should guarantee the ordered clean up of all objects before termination. Also I am not sure quite how it might be made to work if the code is multithreaded which would imply different execution contexts continuing to maintain a hold on raii managed resources even as the thrown thread drops out of the main() function's scope.

Share this post


Link to post
Share on other sites
Quote:
Original post by chairthrower
... just throw an exception. The specific exception which could be thrown from anywhere (ie inside a message pump handling function) would get caught in the main() block and should force the unwinding of the stack in turn releasing all resources that are managed via raii such as stl containers and shared pointer resources.
Exactly. In main you have a try catch around every bit of code in the function, then just throw an exception of the type that only main will catch.
Now when you throw that fatal exception, BOOM everything is destroyed, assuming you're correctly using RAII.

Share this post


Link to post
Share on other sites
Quote:
Original post by shurcool
Okay, I'll adapt my code to have Terminate() set some condition variable like 'running' to false, instead of calling exit() explicitly. That will make the program fall out of the main loop, and eventually get to the bottom of main().


Huh? terminate() is required to never return. There's no standard way to get back to the main loop, or anywhere else, after calling it.

My suggestion: just never use std::exit and never let std::terminate get called. If you want to get to the top level quickly, just throw an exception.

Quote:
Original post by owl
I don't see the problem if the program is going to exit anyway. To be a leak, the program should still be running.


The C++ standard does not guarantee that allocated memory will be freed upon exit. Most OSes take care of it, but I know of at least one that does not. Furthermore, destructors handle more than just memory... they can manage other resources, such as file handles. Those might not be released properly either, even if memory is.

- Kef

Share this post


Link to post
Share on other sites
If your program is at the point of calling exit(), it is supposed to mean that something very serious has happened such that it could not normally exit ("normally exit" = proper cleanup). So the discussion of whether or not exit() handles cleanup is sort of a moot point to begin with, since it should only be called in dire circumstances when program termination is more important than proper program termination.

As others have said, exceptions are a good way to deal with such circumstances. You should be careful though, exceptions are not the answer to everything. Some useful info from MS. Quoted from the page:

Quote:
Because of the nature of exception handling and the extra overhead involved, exceptions should be used only to signal the occurrence of unusual or unanticipated program events. Exception handlers should not be used to redirect the program's normal flow of control. For example, an exception should not be thrown in cases of potential logic or user input errors, such as the overflow of an array boundary. In these cases, simply returning an error code may be simpler and more concise. Judicious use of exception handling constructs makes your program easier to maintain and your code more readable.

Share this post


Link to post
Share on other sites
I disagree somewhat. Certainly you shouldn't use exceptions all the time for general program flow. (At least, not in C++. In languages like Python, it's often idiomatic, depending on what exactly you're doing.) I don't see a problem with using them for, say, validating user input, although they're not the only way to do it. In that case, the overhead of using exception handling is generally inconsequential. They're only a problem if they're executed frequently or in performance-critical code.

And what's with the "such as the overflow of an array boundary" bit? Isn't that exactly the sort of situation you'd throw an exception? That's what std::vector::at() does, isn't it?

Quote:
If your program is at the point of calling exit(), it is supposed to mean that something very serious has happened such that it could not normally exit ("normally exit" = proper cleanup). So the discussion of whether or not exit() handles cleanup is sort of a moot point to begin with, since it should only be called in dire circumstances when program termination is more important than proper program termination.


Not necessarily. exit(0) can be used for a normal exit, and should not be used in abnormal situations. After all, if the situation is abnormal, you should be using exit(1) instead. Suppose you're programming a game and you want a certain key combination to quit. If you're coding in C, why bother bubbling up to main() to quit when you can just do the same thing with exit? In C++, you do have a reason: destructors won't get called. So you need to either have your code written so that you can do a chain of returns back up to main, or throw an exception instead. (Since you're exiting the program, you're not going to be concerned about whatever overhead is involved in throwing an exception. There is an argument that it could be spaghetti code, though, since you're basically doing a non-local goto. So the chain of returns is probably a better idea.)

As for your other point, yes, exiting at all is more important than exiting "properly". But is there any real reason you can't try exiting properly first? If something goes horribly wrong in so doing, like your exception handler also generates an exception, you can always just bail out completely, and you'll end up with the same result. (In fact, this will probably be the default behavior of your code anyway.)

- Kef

Share this post


Link to post
Share on other sites
Quote:
Original post by FurryKef
Quote:
Original post by shurcool
Okay, I'll adapt my code to have Terminate() set some condition variable like 'running' to false, instead of calling exit() explicitly. That will make the program fall out of the main loop, and eventually get to the bottom of main().


Huh? terminate() is required to never return. There's no standard way to get back to the main loop, or anywhere else, after calling it.

My suggestion: just never use std::exit and never let std::terminate get called. If you want to get to the top level quickly, just throw an exception.


I'm pretty sure that shurcool is referring to his own function when he says "Terminate" (hence the capital letter) rather than the standard library's one.

Share this post


Link to post
Share on other sites
Just to make sure you understood my point, I was not saying that exitting at all is more important than exitting properly in the general case, just that in particularly bad situations it might be more important. The classic case is failure to allocate memory; if something like that that happens, you probably don't want your program continuing at all, even if its just to bubble up to main().

As you said, exit() is similiar to goto: just because you can use it doesn't mean you should. I suppose it's somewhat a matter of style.

Quote:
Original post by FurryKef
And what's with the "such as the overflow of an array boundary" bit? Isn't that exactly the sort of situation you'd throw an exception? That's what std::vector::at() does, isn't it?


Yah I dunno what MS was thinking when they said that. It seems like the ideal place to throw an exception: a situation that is not normally expected and the failure should not be handled by the immediate caller. I quoted the text for the purpose of explaining the purpose and overhead involved with exceptions.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this