The Joel on Software Discussion Group (CLOSED)

A place to discuss Joel on Software. Now closed.

This community works best when people use their real names. Please register for a free account.

Other Groups:
Joel on Software
Business of Software
Design of Software (CLOSED)
.NET Questions (CLOSED)
TechInterview.org
CityDesk
FogBugz
Fog Creek Copilot


The Old Forum


Your hosts:
Albert D. Kallal
Li-Fan Chen
Stephen Jones

When new / malloc fails ...

Do you handle the case when new throws an exception or when malloc returns NULL?  If so, how?  Do you think all programs should deal with such a failure, or just certain classes of applications?  If so, which?

I believe that 99 times out of 100, you shouldn't even try to recover from new or malloc failing.  New/malloc failure is like a gunshot wound to the head.  They're highly improbable failures that you really can't do much to recover from.  Frequently, they're self-inflicted gunshot wounds to the head: you corrupted the heap or leaked (often gigabytes) of memory.  Most times, it's far easier to fix your leaks than to simply take them for granted and then try to do something intelligent when they start to suffocate your program.

Because most of the time, you *can't* do something useful.  The correct operation of your program depends on having sufficient amounts of memory.  Even if your program manages to stumble out of an out of memory error in one function, it's bound to run into it again later on.  And again, and again, and again, until really a GPF or call to terminate() is probably the best your program can hope for. 

If you can't recover from new or malloc failing, it makes no sense to check for such a failure.  I believe error checking should be limited to a) realistic exceptional cases that b) you can do something about.  If the user selects a filename that doesn't exist and you can't read from it, that's a realistic exceptional case, and you can do something about it: you can tell the user that the file was not found.  But if malloc fails ... good luck.

The only exception I might make is when an always on, 24-7-365 availability is a program requirement, in which case maybe your exception handler can clear out some caches or something, but kill-and-restart the process is probably the most robust solution possible.

Am I off base?  Tell me your thoughts.
Alyosha` Send private email
Friday, February 25, 2005
 
 
I check for failure on malloc/new failure, but all I do is log the error and any information that might help (line number of error, etc), show an error dialog and exit the program.  No sense trying to keep going.  Something is fubared.
Mr. Fancypants Send private email
Friday, February 25, 2005
 
 
It's one thing to not try to recover from an allocation failure. It's another thing not to handle it at all. Displaying an out-of-memory message and aborting is usually fine for regular user programs. Behaving unpredictably or crashing isn't.

Software which allocates memory should always check for failure, whether the check is done explicitly (checking the return value of malloc) or implicitly (not catching the exception from new).

Note that some older C++ compilers, including the one shipped with Visual Studio 6, don't throw an exception when new fails. If you're targeting such a compiler you need to explicitly check the result of each new.
comp.lang.c refugee
Friday, February 25, 2005
 
 
Ideally, when a program runs out of memory, it should handle the situation gracefully, carefully aborting the current operation, rolling back any half-finished business, and giving the user or remote application a sensible error code.

In my experience, however, it's usually not worth going through all this trouble because most libraries completely melt down in the event of an out-of-memory situation, so even if you write all of your code perfectly, your program will still crash because somewhere, deep in some library you need and can't change, someone's doing:

x = malloc (...);
*x = y;

The only solutions to this are:

1. Accept that if you run out of memory, your program will crash, and do whatever you can to prevent this situation

2. Spend the astronomical amount of effort it would take to rewrite all the code you'll ever depend on to be out-of-memory-safe

3. Switch to a language like Java, where an out-of-memory situation deep in some library causes an OutOfMemoryException to be thrown which your code can catch and handle.


Usually i end up going with Option 1. Here's how i recommend doing it:

* Stress-test your program: Find out what it takes to make your program grow to over 1 GB of memory. Make a game of it; consider it a challenge.
* Once you find that out, write a countermeasure to prevent that "attack".
* Repeat

In summary, it's much easier to write a program that cannot run out of memory than a program that does the right thing when it runs out of memory.

Any doubters who think their code doesn't have out-of-memory problems, try testing it: write a wrapper around malloc() that randomly returns NULL. Dollars to doughnuts your program will fail this test spectacularly.
Mike Schiraldi
Friday, February 25, 2005
 
 
Note that if your system is out of memory, you cannot count on the ability to do ANYTHING else.  You certainly can't call any other function or method reliably (though there may be one exception to this), you can't open up a new dialog, and you can't guarantee that you can log the information.

It _may_ work.  It's more likely to work if you were just trying to malloc a large chunk of memory.  But fundamentally, you are in trouble.  Serious big trouble.

And that's not even counting memory overcommit which most operating systems use these days.

Handle the error as best you can, but fundamentally realise that it is time to panic.
Chris in Edmonton Send private email
Friday, February 25, 2005
 
 
I generally agree, there is one other case when testing for malloc returning NULL is vital, that is when your architecture is not guarenteed to catch and segfault on NULL pointer dereferences. This is generally not an issue with desktop systems, but there are quite a few odd or embedded architectures out there that will happily write or read from  wherever NULL points. Needless to say, this causes all sorts of unpredictable behavior.
John Send private email
Friday, February 25, 2005
 
 
Well there are cases when you should definately check, say if you're photoshop and someone's just tried to load a 10 gigabyte image file or something.
Matt Send private email
Friday, February 25, 2005
 
 
Any kind of situation where the user can request that some kind of memory-intensive processing task happen, some large file be loaded into memory, or similar.

In these cases you should just say 'Sorry, not enough memory to do this' and continue gracefully, rather than just crashing with an error message.
Matt Send private email
Friday, February 25, 2005
 
 
As said above, there's not much good you can do when you get into this state... just make sure your code won't do something *bad*.  Especially if you're writing a server app, make sure you fail securely.
Pickle
Friday, February 25, 2005
 
 
Do you want your product name to show up in Raymond Chen's blog?

If so then don't handle out of memory conditions. That way your app will crash and show up in the 500,000 crash dump reports that are automatically sent to Microsoft.

This is especially true for kernel device drivers... Microsoft, for some strange reason, really gets their shorts in a bunch over 3rd party drivers that bring down Windows.
H&R Blockhead
Friday, February 25, 2005
 
 
I wouldn't check it.

These days, with swap files and all, if your app gets an error with "new" of a relatively small allocation amount, then there is something terribly wrong with the OS.

Remember that when you start to max out on memory, the computer will slow to a halt long before any error, as churning starts to take place effectively paralyzing your machine.

At this point the user is going to kill your app, and perhaps the OS because it will seem like it locked up.

The only exeception is if you think you may need to allocate  a large amount of memory, and then you will need to take further steps on how to handle the large amount of memory.
tatos
Friday, February 25, 2005
 
 
I HATE applications that try to handle things like out-of-memory errors or access violations. 

I'm guessing the majority of these are the result of some sort of decision by non-technical management -- "when the program crashes, the user loses their data so we should do something about that."  Lazy programmer gets the task and figures out that he can intercept the exception and just not exit so he puts in some code with a vague error message and maybe a recommendation that the user save their data (since out-of-memory errors and access violations neeeeever corrupt data) and restart the program.  Typically when I encounter this kind of app, it gets into an unending cycle of message boxes popping up with the same error or maybe some cascading errors making it impossible to close the app without going to Task Manager (and sometimes that doesn't even work too well for some reason).
SomeBody Send private email
Friday, February 25, 2005
 
 
"Note that if your system is out of memory, you cannot count on the ability to do ANYTHING else.  You certainly can't call any other function or method reliably..."

Sure you can -- "new" is just heap memory.

The OP didn't mention his platform, but he should handle the  NULL case because I don't think it's guaranteed to only happen with "out of memory," and it doesn't necessarily mean something is wrong with the OS. The app could be running in a sandbox or virtual machine, or there's a misconfiguration with the user's account, and NULL might be a permissions problem.

If you have "new" all over the place, it's probably not worth all the extra code, but if you're wrapping new with your own memory manager anyway and it's just in one place, you might as well.
Bob
Friday, February 25, 2005
 
 
I say it depends on the application. If you have a zillion users you probably want to be more careful.

Here's what One Major Application I know did:

* In the event of a malloc failing, it at least longjmped back to the main event handler. The current operation might be fubared but you're still processing events.

longjmp is what you all now call throwing an exception, just in C code

* The entire code path for SAVE was carefully tested and NEVER allocated memory. Any memory that SAVE was going to need was preallocated at app startup time and the app would refuse to start if it didn't have enough preallocated memory to save.

The combination of these two principles meant that in an out-of-memory situation a user could ALWAYS save and exit. This was greatly appreciated by many.
Joel Spolsky Send private email
Friday, February 25, 2005
 
 
Have you guys ever had a program run out of memory?

Come on. This is 2005.

Friday, February 25, 2005
 
 
Please refer to "effective C++", 2-nd ed. by Scott Meyers,
item 7: "be prepared for out-of-memory conditions".
nekto
Friday, February 25, 2005
 
 
Thats an eight year old book.  Good thing technology doesn't change too quick.

I also recommend "Effectively Programming the Altair 8800" by Scott Meyers Dad.

:)
The Class Clown
Friday, February 25, 2005
 
 
"longjmp is what you all now call throwing an exception, just in C code

* The entire code path for SAVE was carefully tested and NEVER allocated memory. Any memory that SAVE was going to need was preallocated at app startup time and the app would refuse to start if it didn't have enough preallocated memory to save.

The combination of these two principles meant that in an out-of-memory situation a user could ALWAYS save and exit. This was greatly appreciated by many."

Clever.  As the old saying goes, "Always dig yourself a well before you need to drink from one".  The same thing makes A LOT of sense with respect to "critical-code-path" memory.
Peter Sherman Send private email
Saturday, February 26, 2005
 
 
I'm astounded by those replies suggesting that this wont happen 'these days, with swap files'.  All of the software projects I have worked on have had to consider this case. 
The current product I work with deals with datasets between 2-100GB.  Many of the simple text / data processing utils I've written have had to handle this.  On the win32 platform, you can only allocate ~1.8 GB for your program. Regardless of the amount of phyisical memory or swap space. 

The technique Joel mentioned is similar to what I've worked with.  Its a much bigger headache when you have multiple threads or when calling third party libraries, but its better that crashing and leaving your data in a bad state.

The prellocation strategy I have used in some now-so-critical projects is actually very easy to implement. Just allocate a 'lastChanceBuffer' on startup, and when you get your out of memory failure, free the buffer then abort the operation and optionally initiate shutdown.

But now all our C++ apps use a custom heap implementation which handles the lastChance buffer and the synchronization. ( Unf most third party libraries dont play nice ).

Windows 2000+ has a bunch of nice built in features for testing this.  You can make the OS pretend you only have xx MB of ram. The rtl heap has a great set of tools failing your apps too.
B
Saturday, February 26, 2005
 
 
"You can make the OS pretend you only have xx MB of ram."

Another excellent point.  Test, test, and re-test under "app torture conditions". 

As an aside, I remember when Windows 3.1 would crash on cheaper PC's because the first 640K was stable enough to run DOS apps, but the himem/ems (beyond 1MB) would give subtle memory read errors from time to time -- and people thought that it was Windows' fault!  (Of course, I ALSO remember when Windows would crash on GOOD PC's, and the crash WAS in fact Windows' fault!!! :-) )
Peter Sherman Send private email
Saturday, February 26, 2005
 
 
>> The current product I work with deals with datasets between 2-100GB. <<

Yes, but that is a different case.  There is a large difference between allocating 500 bytes and 5GB.  If you can't allocate 500 bytes with new, your probably not going to make it back to the save routine.  Even if the path to the save routine doesn't allocate memory, what happens when the user moves the mouse over an area of the gui that causes a "new" to occur -- as Joel said, the messages are still being processed.

OTOH, if I was dealing with files in the GB range, then I probably wouldn't be trying to read them into the memory space all at once anyway.  Its apple and oranges.  At that file size, you'll have special routines to figure out the host PC's memory configuration, and probably only read a section of a file at a time.

PS I thought when an exception is thrown and caught, the stack partially unwinds? (unlike a longjump)  Am I wrong?
tatos
Saturday, February 26, 2005
 
 
B: How do you make Windows to pretend it has less memory than it does?

That sort of testing is also quite easy to do on most Unix systems. Bash (and perhaps other shells) has a builtin command called 'ulimit' which lets you set a variety of limits for that instance of the shell and any processes spawned from it.
comp.lang.c refugee
Saturday, February 26, 2005
 
 
ulimit - You can restrict a user-mode process using the "job object".  There is a decent MS UI for controlling this, but I cant seem to remember it.

But what I thinking of was the boot.ini switch /maxmem or /burnmemory. The entire OS thinks you only have 256MB of ram.
BTW you pagefiles dont automagically downsize, so you'll probably want to shrink them too.

fault injection-
verifier can artificailly fail driver memory allocations.
pageheap can be do the same for usermode apps.
B
Saturday, February 26, 2005
 
 
"Do you handle the case when new throws an exception or when malloc returns NULL?"

Yes, always.

"If so, how?"

By having all code using the returned pointer check (p != NULL). This costs about one nano-second on a modern PC.

"Do you think all programs should deal with such a failure, or just certain classes of applications?"

No, not all programs should. Most programs could terminate with a *single* message box giving the __LINE__ and __FILE__ of the error (also saving any important information).

Core software such as libraries, languages and operating systems *should* handle memory allocation failure. In http://www.lingolanguage.com I made the RTL handle allocation failure by making all non-static routines check for null pointers before de-referencing. This also handles cases where classes and structures require multiple allocations to be valid. If some allocations fail, the structure will be incomplete and the allocated parts will be freed, and the entire structure will be returned as empty. I also have run-time options to make the library check for heap leaks on exit.

I also think these techniques should be taken a stage further. Ideally all routines should be NULL proof, indeed MS released some new versions of the classic C string.h functions that are NULL proof. Routines should also use IsBadReadPtr() and IsBadWritePtr() to validate parameters before use, my only concern being I don't know the time cost of these routines. Testing should also include failure of the 1st call to malloc, the 2nd call to malloc etc. But I have not yet used these last techniques throughout Lingo.
Bill Send private email
Sunday, February 27, 2005
 
 
Joel: "* The entire code path for SAVE was carefully tested and NEVER allocated memory. Any memory that SAVE was going to need was preallocated at app startup time and the app would refuse to start if it didn't have enough preallocated memory to save. "


This begs an interesting question: When does the operating system's memory allocator actually **fail** an allocation request?

Okay, so I pre-allocate a save buffer, and write a special extra-conservative save routine. I pass my file image to the OS's file system to be written to disc. Except, the file system now needs to temporarily cache some directory tree structures as it traverses to the folder into which my save is destined.

But since the system is now officially "out of memory" there's no space to allocate a temporary cache. So the save fails anyway.

Okay, okay, so it might not work precisely like this. But we don't generally know how system software works in detail. Particularly if we're saving to a storage device provided by a third party driver.

Sorry to spoil the party guys, but hence my question: When does the OS's memory allocator actually return a failure signal to a program? When we're actually out of memory to fulfil the request? Or, when we're somewhere-near-to-out-of-memory?

I don't really expect people to be able to answer this, I'm really just highlighting the mild skepticism I feel about implmenting special saves.
Jonathan Send private email
Sunday, February 27, 2005
 
 
Saving your current work generally means writing to a pre-existing file.  Yes, not _always_, but really it ought to.  Either the file you specified, or when you didnt specifiy a file, a temp file.  Windows FS's dont required additional allocations to write to an existing file, unless its growning.  Being out of disk space, is another problem.

Just because you program is out of memory, doesnt mean the OS is. The OS has it's own special save area.
When the OS is truly out of memory, thats a different story. 
NT has a limited memory space( either 2GB or 1GB ). Of that space certain ranges are only for cache/pool/pagetables.
out of PTE's -> bugcheck.
I only speak of Win2K+, who knows with other OS's

Yes, this issue is near and dear to my heart.

SIMILAR QUESTION - What do you do when you are out of diskspace on a save?
b
Monday, February 28, 2005
 
 
Do like a database does: Rollback.
Jonathan
Monday, February 28, 2005
 
 
"Most programs could terminate with a *single* message box giving the __LINE__ and __FILE__ of the error (also saving any important information)."

And how exactly are you going to create a message box when you are out of memory? That was sort of the point.

Monday, February 28, 2005
 
 
As I recall, the Win32 MessageBox function is special in that it doesn't require any additional memory. All its resources are pre-allocated when Windows starts. That's why you can use it as an error notification when nothing else would work. I think Raymond Chen once blogged about this issue...
Chris Nahr Send private email
Monday, February 28, 2005
 
 
"And how exactly are you going to create a message box when you are out of memory? That was sort of the point."

Use MB_SYSTEMMODAL which is designed for this purpose.

Also you could use the same technique in your own app. Just pre-allocate the HWND and buffers beforehand.
Bill Send private email
Tuesday, March 01, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz