Why does new allocate more memory than there is??

A

Alan Gifford

I wrote a program to make sure that new would throw a bad_alloc
exception if more memory was requested than was available. On my
system, new allocates up to 2931 MBs of memory (I don't have that
much, not even with swap) before throwing an exception. When the
program tries to fill in the allocated memory with data, it is killed
after using up the available memory.

Does anyone know why the exception is not being thrown until well
after all memory has been allocated? Here is the code...

#include <new.h>
#include <iostream.h>
#include <unistd.h>

#define ARRAY_SIZE1 131072 // # of doubles to take up 1MB of memory
#define ARRAY_SIZE2 3072 // How many MBs do you want to attempt?

int main(void) {
cout << "This is a test to catch for problems with new allocating memory."
<< endl;
cout << "You might want to watch your memory resource meter."
<< endl;
sleep(2);

double* dblPtr[ARRAY_SIZE2];

cout << "\nAttempting to allocate "
<< ((sizeof(double) * ARRAY_SIZE1 * ARRAY_SIZE2) / (1024 * 1024))
<< " megabytes." << endl;

int arraySize = ARRAY_SIZE2;
for (int i=0; i < ARRAY_SIZE2; i++) {
try {
dblPtr = new double[ARRAY_SIZE1];
if (dblPtr == NULL) {
cout << "\nError, pointer was set to NULL." << endl;
}
}
catch (bad_alloc ex) {
cout << "\nException caught: " << ex.what() << endl;
arraySize = i - 1;
cout << "Setting arraySize to " << arraySize << endl;
break;
}
}

cout << "\nOstensibly allocated "
<< ((sizeof(double) * ARRAY_SIZE1 * arraySize) / (1024 * 1024))
<< " megabytes of memory." << endl;
cout << "Let's see what happens when we try to fill it." << endl;
sleep(2);

cout << "\nBeginning filling arrays. If the program is killed, it means the"
<< endl;
cout << "program was not actually able to fill the memory it has been "
<< "allocated." << endl;

double dummy = 6.67259e-11;
double* tmp;
for (int i=0; i < arraySize; i++) {
tmp = dblPtr;
for (int j=0; j < ARRAY_SIZE1; j++) {
tmp[j] = dummy;
}
}
return 0;
}
 
C

cpp_weenie

Alan Gifford said:
I wrote a program to make sure that new would throw a bad_alloc
exception if more memory was requested than was available. On my
system, new allocates up to 2931 MBs of memory (I don't have that
much, not even with swap) before throwing an exception. When the
program tries to fill in the allocated memory with data, it is killed
after using up the available memory.

Does anyone know why the exception is not being thrown until well
after all memory has been allocated? Here is the code...

#include <new.h>
#include <iostream.h>
#include <unistd.h>

#define ARRAY_SIZE1 131072 // # of doubles to take up 1MB of memory
#define ARRAY_SIZE2 3072 // How many MBs do you want to attempt?

int main(void) {
cout << "This is a test to catch for problems with new allocating memory."
<< endl;
cout << "You might want to watch your memory resource meter."
<< endl;
sleep(2);

double* dblPtr[ARRAY_SIZE2];

cout << "\nAttempting to allocate "
<< ((sizeof(double) * ARRAY_SIZE1 * ARRAY_SIZE2) / (1024 * 1024))
<< " megabytes." << endl;

int arraySize = ARRAY_SIZE2;
for (int i=0; i < ARRAY_SIZE2; i++) {
try {
dblPtr = new double[ARRAY_SIZE1];
if (dblPtr == NULL) {
cout << "\nError, pointer was set to NULL." << endl;
}
}
catch (bad_alloc ex) {
cout << "\nException caught: " << ex.what() << endl;
arraySize = i - 1;
cout << "Setting arraySize to " << arraySize << endl;
break;
}
}

cout << "\nOstensibly allocated "
<< ((sizeof(double) * ARRAY_SIZE1 * arraySize) / (1024 * 1024))
<< " megabytes of memory." << endl;
cout << "Let's see what happens when we try to fill it." << endl;
sleep(2);

cout << "\nBeginning filling arrays. If the program is killed, it means the"
<< endl;
cout << "program was not actually able to fill the memory it has been "
<< "allocated." << endl;

double dummy = 6.67259e-11;
double* tmp;
for (int i=0; i < arraySize; i++) {
tmp = dblPtr;
for (int j=0; j < ARRAY_SIZE1; j++) {
tmp[j] = dummy;
}
}
return 0;
}


A range of virtual addresses is set aside for your use by new, but pages of
those addresses aren't backed by real storage (either physical mem. or swap)
until actually touched...

Hmmm... I just had an interesting thought. If you had been allocating a
class type with a non-trivial constructor (rather than a double), I suppose
the memory when have been touched right away during construction of the
objects and then you would have seen an exception right away.

Any thoughts on that anyone? Any deeper issues here that I'm not
considering right off-the-cuff?
 
A

Alan Gifford

cpp_weenie said:
A range of virtual addresses is set aside for your use by new, but pages of
those addresses aren't backed by real storage (either physical mem. or swap)
until actually touched...

Hmmm... I just had an interesting thought. If you had been allocating a
class type with a non-trivial constructor (rather than a double), I suppose
the memory when have been touched right away during construction of the
objects and then you would have seen an exception right away.

Any thoughts on that anyone? Any deeper issues here that I'm not
considering right off-the-cuff?

Well, I'm not so much concerned with what goes on behind the scenes to
create the problem. I guess I am asking why it happens this way. I
don't really understand the purpose of a bad_alloc exception if it's
not going to be thrown every time there was a bad allocation attempt.

I am not an experienced programmer, so I don't know for sure that this
is the main purpose of the bad_alloc exception, but it certainly seems
like it is not operating the way it's supposed to.

Bottom line: I'd just like to know, am I wrong to think that bad_alloc
should be thrown in this case, or is the compiler doing something
wrong? I've performed this test on several Linux machines as well as
a Mac running OS 10.1, and they both have the same error. I was using
gcc (g++) in all systems.

Thanks!
Alan
 
D

Dave

Alan Gifford said:
Well, I'm not so much concerned with what goes on behind the scenes to
create the problem. I guess I am asking why it happens this way. I
don't really understand the purpose of a bad_alloc exception if it's
not going to be thrown every time there was a bad allocation attempt.

I am not an experienced programmer, so I don't know for sure that this
is the main purpose of the bad_alloc exception, but it certainly seems
like it is not operating the way it's supposed to.

Bottom line: I'd just like to know, am I wrong to think that bad_alloc
should be thrown in this case, or is the compiler doing something
wrong? I've performed this test on several Linux machines as well as
a Mac running OS 10.1, and they both have the same error. I was using
gcc (g++) in all systems.

Thanks!
Alan

I think that your expectations are very reasonable but, nevertheless, I
don't think you will necessarily see the behavior you expect for the reason
I stated. I am in total agreement with you that a bad_alloc being thrown
well after the fact doesn't seem to make a lot of sense. Nevertheless,
reality is that individual pages may not be backed by actual storage until
touched. Though you and I find your compiler's behavior illogical, I don't
believe it is "wrong" in the sense that there is a bug. I think it is
exhibiting the behavior intended by the compiler writers.

I'll leave it to others more knowledgeable than I to comment on why pages
might not be backed by actual storage until they're touched. I'd be very
interested to know the reason.
 
T

tom_usenet

Well, I'm not so much concerned with what goes on behind the scenes to
create the problem. I guess I am asking why it happens this way. I
don't really understand the purpose of a bad_alloc exception if it's
not going to be thrown every time there was a bad allocation attempt.

I am not an experienced programmer, so I don't know for sure that this
is the main purpose of the bad_alloc exception, but it certainly seems
like it is not operating the way it's supposed to.

Bottom line: I'd just like to know, am I wrong to think that bad_alloc
should be thrown in this case, or is the compiler doing something
wrong? I've performed this test on several Linux machines as well as
a Mac running OS 10.1, and they both have the same error. I was using
gcc (g++) in all systems.

The problem is that bad_alloc doesn't fit well with modern virtual
paging memory systems. On some systems, for performance reasons,
memory isn't actually set aside when you call malloc/new, since where
the pages of memory will come from (disk, main memory, cache?) is only
decided when you try to access the memory. This makes it impossible
for the system to know for sure whether it can allocate a chunk of
memory when you make the allocation, since, e.g., you might deallocate
another chunk before trying to access this one, thus making room
available.

This means that you have to rely on systems other than bad_alloc to
handle low memory on some modern OSes. On modern OSes, you'll be into
major performance problems long before you actually run out of memory
(paging to disk will cause hard disk thrashing), so you need to
monitor how much memory your program is using and how much is
available using OS specific functions.

It's a shame that bad_alloc is largely useless on modern desktop
platforms.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,733
Latest member
LonaMonzon

Latest Threads

Top