• Advertisement
Sign in to follow this  

c++ memory management

This topic is 3846 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I came along this very interesting site : site and now I am wondering where I can find resources describing memory management in C++ ? What are the different available alternatives? I am sure there is more than just calling new and delete.

Share this post


Link to post
Share on other sites
Advertisement
new and delete must be used very carefully, otherwise memory leakage will ensue. But yes, there is more beyond new and delete... there is malloc() (and related functions). They are used to explicitly allocate memory for data. One just has to remember to free() the memory to avoid memory leaks. I'm pretty sure there are other things, but they are beyond me.

Share this post


Link to post
Share on other sites
Memory management is a pretty broad subject.

Direct3D has a memory management system which you interact with via AddRef() and Release(). You can use smart pointers to manage pointers that point to dynamically allocated memory such that it is not leaked. You can overload new and delete in order to manually manage the location and organisation of memory allocations.

Which of these options has you interested?

Share this post


Link to post
Share on other sites
I was more referring to what happens inside new and delete. Apparently there are different strategies there, if I use the terms of www.memorymanagement.org you have :
First Fit, Buddy System. I am sure there are others. So how does this translate in C++ ? does this relates the different allocators one can give to STL containers ?


Share this post


Link to post
Share on other sites
Quote:

I am sure there are others. So how does this translate in C++ ?

Unspecified. The C++ standard does not mandate the method used to lookup/allocate/manage/release memory acquired via new and freed with delete. Most implementations will likely fall through to the underlying OS allocation routines, which may use whatever allocation scheme makes sense.

Quote:

does this relates the different allocators one can give to STL containers ?

The allocators are used to customize how memory is allocated for SC++L containers, but not what new and delete actually end up doing. So they're somewhat related, but not really...

Share this post


Link to post
Share on other sites
From what I understand the OS in terms of memory management doesn't deal with things smaller than a page : something around 4Kb or more.
So I was wandering how these pages where managed by the C++ allocator/deallocator, and what options/strategies were available.

Share this post


Link to post
Share on other sites
Well, that depends on the OS. Even if they use that kind of granularity internally, most will expose some method of carving up blocks into smaller chunks or something; most OSs probably have more memory management functionality that you realize. After all, recall that all of the C++ standard library features (and all features of other languages, too) are ultimately implemented on top of the functionality provided by the OS. On modern non-embedded systems, at least.

That said, all of this stuff varies widely -- as I said before, there are few restrictions placed upon an implementation by the C++ specification. You can use Google to search for memory management information (resources on OS theory are a useful source). You can also consult the documentation for the core APIs for your OS (such as the Platform SDK documentation on MSDN). To get an idea of what you might want to look for there, write up a simple C++ program that performs a dynamic allocation and use your debugger to step into it. Follow it far enough, just like fopen() and the like, you will arrive at an OS function eventually and possibly gain some insight into what implementation your development environment is employing.

Share this post


Link to post
Share on other sites
Well if you want to take a look to alternative allocation strategy there's boost::pool. In the library documentation it's described pool principles and simple segregate storage. Instead if you want to see how a professional C++ compiler manage memory allocation take a look at gcc source code.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
Quote:

does this relates the different allocators one can give to STL containers ?

The allocators are used to customize how memory is allocated for SC++L containers, but not what new and delete actually end up doing. So they're somewhat related, but not really...

Do note that STL (SC++L looks overly convoluted :\ ) allocators are not guaranteed to be used by "list" and associative containers since they are node based. This is because STL allocators only allocate T type objects not the ListNode<T> or MapNode<T> objects that node based containers need.

Share this post


Link to post
Share on other sites
Quote:
Original post by T1Oracle
Quote:
Original post by jpetrie
Quote:

does this relates the different allocators one can give to STL containers ?

The allocators are used to customize how memory is allocated for SC++L containers, but not what new and delete actually end up doing. So they're somewhat related, but not really...

Do note that STL (SC++L looks overly convoluted :\ ) allocators are not guaranteed to be used by "list" and associative containers since they are node based. This is because STL allocators only allocate T type objects not the ListNode<T> or MapNode<T> objects that node based containers need.


Wrong. SC++L allocators must expose Allocator::rebind<U>::other, also an allocator, which allows an allocator to be adapted to a new type U, where U can be, say, a node type.

[Edited by - MaulingMonkey on June 16, 2007 9:26:09 AM]

Share this post


Link to post
Share on other sites
The new and delete that you use are defined by the platform. Really the OS can do whatever it wants when it is asked to allocated some memory, so long as new and delete behave as expected. A new call filters down to the operating system kernel allocation call which returns the pointer back up to new which returns it to you. We can do very little about what happens when you request allocation, be it malloc, realloc etc or new.

What we can do however is request a big block of memory (that we think is going to be big enough for our application) and managed it ourselves. This is tricky, but definately doable, but doesn't provide a great deal of benefits to the average application. The default implementation of new and underlying memory management is fast and efficient, but is designed to be very general purpose. However it can be improved upon slightly.

An example of this is how my memory manager works. I wrote this such that it saved memory and was quicker at allocating than the default strategy. I allocated a pool when the MM initialises and then partition that pool off into my own pages.

**FYI when you make an allocation with new there is a 32 byte header on the front of it that contains information about it's sizeand such. This is the information used by delete.**

The reason you allocate a pool is because it means that you only make an allocation through the OS (which is generally slow, though as mentioned before is still perfectly fine for 99% of applications). What you can now do is rewrite new such that it makes a call to your memory manager. The MM now 'allocates' some memory in the preallocated pool (almost instantly) and returns a pointer to it, which propogates to the pointer you are assigning to in your application.

If you were to manage a simple pool that could cater for allocations of a variety of sizes, you would need to maintain a similar 32 byte header on the front of each allocation. In this scenario only faster allocation is acheived. However if you were to fragment the pool into multiple pages, some pages could be created to only store allocations of a certain size and therefore a header is not actually needed. In this scenario you have fast allocation and a reduction in memory usage due to the lack of headers.

There are some interesting tasks to consider when managing a 'general pool' as i call it, that is a pool that can store different sizes of allocations. You have to maintain a linked lists that is the headers in front of the allocations. When an allocation comes in you have some checking to do. In mine i iterated through the allocation headers until i find one that was the perfect size, big enough (but not massive) or one that could be split into a partition of a certain size and the remainder. Once a suitable block is found i set it up for the allocation request. I then make a pass over the entire linked list looking for sequences of adjacent unused blocks and then merge them all togther.

Finally i would like to stress that there isn't much need to do this unless you have an application that does alot of per-frame/run-time allocations. A typical application won't do alot of this, but things such as physics simulations and research tools might well do. Also, applications that are targetted for platforms where there is little memory available (such as embedded devices) would benefit from a system like this, however even then it is important to determine whether the memory saved is worth the effort.

Hope that helps,

If you have any questions, i can try and answer!

Dave

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
Quote:
Original post by T1Oracle
Quote:
Original post by jpetrie
Quote:

does this relates the different allocators one can give to STL containers ?

The allocators are used to customize how memory is allocated for SC++L containers, but not what new and delete actually end up doing. So they're somewhat related, but not really...

Do note that STL (SC++L looks overly convoluted :\ ) allocators are not guaranteed to be used by "list" and associative containers since they are node based. This is because STL allocators only allocate T type objects not the ListNode<T> or MapNode<T> objects that node based containers need.


Wrong. SC++L allocators must expose Allocator::rebind<U>::other, also an allocator, which allows an allocator to be adapted to a new type U, where U can be, say, a node type.


Quote:
Original post by Scott Meyers Effective STL Page 48 paragraph 4 sentence 3
most of the standard containers never ask their associated allocator for memory. Never.

Share this post


Link to post
Share on other sites
Here is a question for you: are you more likely to have performance problems if you assume that the memory manager is slow, or if you know you're using a state of the art memory manager?

In my opinion, trying to optimize the memory manager is premature optimization at best. A better strategy is to use the default memory manager but code in a way that makes it easy to change your mind on memory allocations -- if the profiler tells you that you must.

Emil Dotchevski
http://www.revergestudios.com/reblog/index.php?n=ReCode.ReCode

Share this post


Link to post
Share on other sites
Thanks for your answers.
I hadn't logged into gamedev for quite some time, which explains this late answer.

I am not thinking of writing my own MM at this time. It would be pointless as I still don't understand all the aspects of the subject.

However, I am trying to get a better grasp at what is happening under the hood when I write new or delete. From what I have read these operations are none trivial ones and can be very time consuming: finding the next free block with the most appropriate size, defragmenting the memory...

But, the thing that most struck me from all my readings on the subject, is the apparent common statement that MM through GCs give better performances. This is really puzzling me. If this is true then why continue to bother with news and deletes, if in the end it is more work and less efficient. I would like to know your opinion on the subject.

Share this post


Link to post
Share on other sites
Garbage collectors can give better performance than the usual manual heap management allocators; it depends on the algorithms used and the patterns of memory access. Some garbage collectors can periodically compact the memory being used so that there is better cache coherence and fewer memory pages used. This compacting is how the garbage collection is actually accomplished, since only memory currently in use is copied and all the pointers are updated. This also simplifies allocations, since the memory cannot be fragmented after being compacted like this.

Completely avoiding dynamic memory management is even faster, but it requires more design work and imposes limitations on the system. Still, video games often have memory budgets for different systems to strictly control memory use, especially on console systems, avoiding general purpose dynamic memory allocation as much as possible (memory management might be done using pools of fixed size objects, which has very little cost but places restrictions on how the memory is used).

There doesn't seem to be any ideal garbage collector, so new and delete are still around (though they tend not to be in modern languages). Most garbage collectors require language level support for garbage collection, and C++ currently doesn't have that.

Share this post


Link to post
Share on other sites
Quote:
Original post by EmilDotchevski
Here is a question for you: are you more likely to have performance problems if you assume that the memory manager is slow, or if you know you're using a state of the art memory manager?


I can't speak in context of the original poster but I can speak in context for console games. If a game doesn't approach memory management from the initial design of the engine and each game then that game will have problems in the end.

The most efficient memory management strategy I've seen was for a ps2 game. The scheme was to have one guy in the studio manage memory range. If you wanted to allocate memory you'd have to go talk to the memory guy and request a certain amount of memory. He would then give you a range that you could allocate from.

-= Dave

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement