.NET Forum / .NET Framework / Performance / January 2005
How much memory do managed code objects take, really?
|
|
Thread rating:  |
Marcos Stefanakopolus - 07 Jan 2005 17:31 GMT Is there a general method for finding out, to the byte, how much memory is consumed by a given instance of an object?
I'm in the process of designing a scientific simulation program (to simulate planetary formation, tectonics, etc). To simulate an earth-sized planet with any nice degree of resolution takes an astonishing number of objects. For instance, tiling the earth with triangles of size 50 kilometers just to do a reasonable tectonic simulation takes almost 440,000 triangles. That's not bad, but subsequent refinements down to 1 km blow that number up to something like 1.7x10^9 triangles (or maybe only 6x10^8 if I ignore everything that's underwater).
Clearly, in this type of application, every byte of memory must be carefully considered; even a single extra byte of padding somewhere in a data structure can end up costing you hundreds of MB of memory. I need to decide whether to implement these triangles and their vertices as managed C# objects (much easier for me), or as unmanaged C++ objects (in order to tightly control memory so the model fits into RAM).
In the unmanaged C++ world, I know exactly how to count the bytes my classes will take up. In the managed C# world, with the overhead that the framework imposes in order to be able to move objects around, do garbage collection, etc., I have no idea. If I define:
class WorldPoint { public float x,y,z; // in C++ this costs 12 bytes }
class WorldTriangle { WorldPoint *vertices[3]; // in C++, this costs 12 bytes, too, in the form of pointers. }
In C++, if I build 1 WorldTriangle, I've used 12 bytes for pointers to the vertices, and 36 bytes for the vertex objects themselves. In C#, How much memory have I really used?
Jon Skeet [C# MVP] - 07 Jan 2005 21:07 GMT > Is there a general method for finding out, to the byte, how much memory is > consumed by a given instance of an object? The method I usually use is to create a test program which creates an empty array of hundreds of thousands of references, finds out the amount of memory used, fills in the array with hundreds of thousands of objects, finds out the amount of memory *then* used, and then looks at the difference between the two. Use GC.KeepAlive to make sure the array (and thus the instances) don't get collected.
You can also work it out manually for simple cases, of course.
<snip>
> In the unmanaged C++ world, I know exactly how to count the bytes my classes > will take up. In the managed C# world, with the overhead that the framework [quoted text clipped - 13 lines] > vertices, and 36 bytes for the vertex objects themselves. In C#, How much > memory have I really used? Each object has an overhead of 8 bytes. Each float takes 4 bytes. Each reference takes 4 bytes. Each one-dimensional array takes the size of the element * number of elements + 16 bytes (I *think* - check that, as there could be more stuff I've missed, particularly for non-zero lower bounds).
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Jon Skeet [C# MVP] - 07 Jan 2005 21:12 GMT <snip>
> > In C++, if I build 1 WorldTriangle, I've used 12 bytes for pointers to the > > vertices, and 36 bytes for the vertex objects themselves. In C#, How much [quoted text clipped - 5 lines] > there could be more stuff I've missed, particularly for non-zero lower > bounds). Sorry, I meant to mention - there are also potential gaps due to padding between elements. That is variable depending on the layout used - which is one reason to test your estimate using a program.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Marcos Stefanakopolus - 07 Jan 2005 23:50 GMT >> The method I usually use is to create a test program which creates an >> empty array of hundreds of thousands of references, finds out the >> amount of memory used, fills in the array with hundreds of thousands of >> objects, finds out the amount of memory *then* used, and then looks at >> the difference between the two. And how do you do the "finds out the amount of memory used" part? Do you have some code you could share? Somehow I doubt that what Task Manager will tell me is either precise or accurate enough.
>> > In C++, if I build 1 WorldTriangle, I've used 12 bytes for pointers to >> > the [quoted text clipped - 4 lines] >> Each object has an overhead of 8 bytes. Each float takes 4 bytes. Each >> reference takes 4 bytes. So you're saying that all reference types have an 8-byte overhead, while value types have a 0 byte overhead? If true, that's probably sufficient for my purposes.
> Sorry, I meant to mention - there are also potential gaps due to > padding between elements. Yes, I'm aware of that potential issue. Fortunately, there's [StructLayoutAttribute(LayoutKind.Explicit)] to help with that.
Jon Skeet [C# MVP] - 08 Jan 2005 07:38 GMT > >> The method I usually use is to create a test program which creates an > >> empty array of hundreds of thousands of references, finds out the [quoted text clipped - 5 lines] > have some code you could share? Somehow I doubt that what Task Manager will > tell me is either precise or accurate enough. GC.GetTotalMemory.
> >> > In C++, if I build 1 WorldTriangle, I've used 12 bytes for pointers to > >> > the [quoted text clipped - 8 lines] > value types have a 0 byte overhead? If true, that's probably sufficient for > my purposes. That's true - but don't forget that if a value type taking, say, 12 bytes and you have 5 copies of it (60 bytes in total), you'd be better off with 5 references (20 bytes) and one instance (20 bytes).
I almost never use memory usage to decide whether to use reference types or value types though - normal semantics should almost always dictate that.
> > Sorry, I meant to mention - there are also potential gaps due to > > padding between elements. > > Yes, I'm aware of that potential issue. Fortunately, there's > [StructLayoutAttribute(LayoutKind.Explicit)] to help with that. Yup. (Not sure why it's named StructLayoutAttribute when it can apply to classes, but never mind.)
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Bruno Jouhier [MVP] - 08 Jan 2005 08:17 GMT >> >> The method I usually use is to create a test program which creates an >> >> empty array of hundreds of thousands of references, finds out the [quoted text clipped - 33 lines] > types or value types though - normal semantics should almost always > dictate that. Right! Two more reasons to follow normal semantics (unless you have a very good reason to do otherwise) :
If you want to use references and share instances to benefit from the memory savings, you need some kind of indexing mecanism (hash table, array) to "intern" identical values and produce a unique reference. So, you have to take this into account in your memory computation.
When you run on a 64 bit OS, the pointers will double in size!
Bruno.
>> > Sorry, I meant to mention - there are also potential gaps due to >> > padding between elements. [quoted text clipped - 4 lines] > Yup. (Not sure why it's named StructLayoutAttribute when it can apply > to classes, but never mind.) Marcos Stefanakopolus - 10 Jan 2005 20:41 GMT >> And how do you do the "finds out the amount of memory used" part? Do you >> have some code you could share? Somehow I doubt that what Task Manager >> will >> tell me is either precise or accurate enough. > > GC.GetTotalMemory. Beautiful, thanks!
>> So you're saying that all reference types have an 8-byte overhead, while >> value types have a 0 byte overhead? If true, that's probably sufficient [quoted text clipped - 4 lines] > bytes and you have 5 copies of it (60 bytes in total), you'd be better > off with 5 references (20 bytes) and one instance (20 bytes). Can't I have the best of both worlds? If I make my vertex type a value type, and go to the bother of working with references explicitly in my triangle class, wouldn't I end up with (in your example) 12+20 bytes rather than 20+20? If there's some subtle reason why not, please let me know.
> I almost never use memory usage to decide whether to use reference > types or value types though - normal semantics should almost always > dictate that. Do you regularly work with applications that have quantities of objects best expressed in exponential notation? :-)
>> > Sorry, I meant to mention - there are also potential gaps due to >> > padding between elements. [quoted text clipped - 4 lines] > Yup. (Not sure why it's named StructLayoutAttribute when it can apply > to classes, but never mind.) If I had to guess, I'd say it's because they created it for the purpose of making C# structs also support C-style union semantics, but then realized that there was no particular reason why that attribute couldn't also apply to classes.
Jon Skeet [C# MVP] - 10 Jan 2005 22:46 GMT > > That's true - but don't forget that if a value type taking, say, 12 > > bytes and you have 5 copies of it (60 bytes in total), you'd be better [quoted text clipped - 4 lines] > triangle class, wouldn't I end up with (in your example) 12+20 bytes rather > than 20+20? If there's some subtle reason why not, please let me know. If you make Vertex a value type, you could sort of get the best of both worlds by using Object when you want to use it as a sort of reference type - so it causes boxing - or you might want to have both a struct and a class which just contains the struct as its only field. That way you could explicitly say in your code which you wanted to use at any one time.
> > I almost never use memory usage to decide whether to use reference > > types or value types though - normal semantics should almost always > > dictate that. > > Do you regularly work with applications that have quantities of objects best > expressed in exponential notation? :-) No - that's why I didn't express is as an absolute rule :)
> > Yup. (Not sure why it's named StructLayoutAttribute when it can apply > > to classes, but never mind.) [quoted text clipped - 3 lines] > that there was no particular reason why that attribute couldn't also apply > to classes. Possibly. It's far from the worst decision in the framework, of course
:)
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|