3

I've got a very basic application that boils down to the following code:

char* gBigArray[200][200][200];
unsigned int Initialise(){  
    for(int ta=0;ta<200;ta++)
        for(int tb=0;tb<200;tb++)
            for(int tc=0;tc<200;tc++)
                gBigArray[ta][tb][tc]=new char;
    return sizeof(gBigArray);
}

The function returns the expected value of 32000000 bytes, which is approximately 30MB, yet in the Windows Task Manager (and granted it's not 100% accurate) gives a Memory (Private Working Set) value of around 157MB. I've loaded the application into VMMap by SysInternals and have the following values:

I'm unsure what Image means (listed under Type), although irrelevant of that its value is around what I'm expecting. What is really throwing things out for me is the Heap value, which is where the apparent enormous size is coming from.

What I don't understand is why this is? According to this answer if I've understood it correctly, gBigArray would be placed in the data or bss segment - however I'm guessing as each element is an uninitialised pointer it would be placed in the bss segment. Why then would the heap value be larger by a silly amount than what is required?

trincot
  • 317,000
  • 35
  • 244
  • 286
R4D4
  • 1,382
  • 2
  • 13
  • 34
  • 1
    `new char` - you're not creating a new single-byte character. you're creating a char object, which has metadata and other overhead to account for. The array is just 200x200x200 pointers to those objects, but the objects themselves obviously are taking up 157-30 = 127 megabytes of memory. – Marc B Jul 21 '11 at 21:32
  • Why do you have an array of char pointers in the first place? Why not simply an array of chars? – fredoverflow Jul 22 '11 at 06:31

8 Answers8

4

It doesn't sound silly if you know how memory allocators work. They keep track of the allocated blocks so there's a field storing the size and also a pointer to the next block, perhaps even some padding. Some compilers place guarding space around the allocated area in debug builds so if you write beyond or before the allocated area the program can detect it at runtime when you try to free the allocated space.

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
2

you are allocating one char at a time. There is typically a space overhead per allocation

Allocate the memory on one big chunk (or at least in a few chunks)

pm100
  • 48,078
  • 23
  • 82
  • 145
1

Do not forget that char* gBigArray[200][200][200]; allocates space for 200*200*200=8000000 pointers, each word size. That is 32 MB on a 32 bit system.

Add another 8000000 char's to that for another 8MB. Since you are allocating them one by one it probably can't allocate them at one byte per item so they'll probably also take the word size per item resulting in another 32MB (32 bit system).

The rest is probably overhead, which is also significant because the C++ system must remember how many elements an array allocated with new contains for delete [].

orlp
  • 112,504
  • 36
  • 218
  • 315
  • Except he's not calling `new[]` here, so it's undefined to call `delete[]`. The non-array version of `delete` knows the size from the type passed in. – Karl Bielefeldt Jul 21 '11 at 21:43
1

Owww! My embedded systems stuff would roll over and die if faced with that code. Each allocation has quite a bit of extra info associated with it and either is spaced to a fixed size, or is managed via a linked list type object. On my system, that 1 char new would become a 64 byte allocation out of a small object allocator such that management would be in O(1) time. But in other systems, this could easily fragment your memory horribly, make subsequent new and deletes run extremely slowly O(n) where n is number of things it tracks, and in general bring doom upon an app over time as each char would become at least a 32 byte allocation and be placed in all sorts of cubby holes in memory, thus pushing your allocation heap out much further than you might expect.

Do a single large allocation and map your 3D array over it if you need to with a placement new or other pointer trickery.

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
0

Edited out of the above post into a community wiki post:

As the answers below say, the issue here is I am creating a new char 200^3 times, and although each char is only 1 byte, there is overhead for every object on the heap. It seems creating a char array for all chars knocks the memory down to a more believable level:

char* gBigArray[200][200][200];
char* gCharBlock=new char[200*200*200];
unsigned int Initialise(){  
    unsigned int mIndex=0;
    for(int ta=0;ta<200;ta++)
        for(int tb=0;tb<200;tb++)
            for(int tc=0;tc<200;tc++)
                gBigArray[ta][tb][tc]=&gCharBlock[mIndex++];
    return sizeof(gBigArray);
}
0

Allocating 1 char at a time is probably more expensive. There are metadata headers per allocation so 1 byte for a character is smaller than the header metadata so you might actually save space by doing one large allocation (if possible) that way you mitigate the overhead of each individual allocation having its own metadata.

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
0

Perhaps this is an issue of memory stride? What size of gaps are between values?

Brent Arias
  • 29,277
  • 40
  • 133
  • 234
0

30 MB is for the pointers. The rest is for the storage you allocated with the new call that the pointers are pointing to. Compilers are allowed to allocate more than one byte for various reasons, like to align on word boundaries, or give some growing room in case you want it later. If you want 8 MB worth of characters, leave the * off your declaration for gBigArray.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94