Postgres uses memory context to manage its memory.
One advantage of doing so that I can think of is to divide all memory allocations into different contexts so that allocations in a context can be freed in bulk. However, I never met a similar concept in C++. Is it because in C++ there are smart pointers thus don't need such context? If Postgres was developed in C++, would it use smart pointers instead of memory context?
C++ smart pointers provide no such benefit, since they still fundamentally perform individual deallocations (allocate n items, perform n deallocations), while the memory contexts here allow for bulk deallocation (allocate n items, perform 1 deallocation).
This reduced cost of deallocation (often combined with reduced overhead for performing the allocations) is the whole point of region based memory management (the general term for this strategy).
C++ code could use this strategy, e.g. with a custom allocator that is reference counted and contains the bulk allocation, with each suballocation's deleter decrementing the reference count, with the bulk allocation being released in bulk when the count drops to zero, but it's tricky to do safely, especially if pointers might reference the data inadvertently; C has it easier since all such pointers are explicit, there's no issue with determining when constructors/destructors should occur (they don't exist), etc.
Essentially, this is a trick used in very specific contexts where all pointers involved are either internal to the region in which they were allocated, or not involved in region-based management. And trying to extend it to modern C++-style smart pointers and allocators introduces the same complexity those smart pointers are trying to avoid, so it's not usually worth bothering.
Point is, nothing prevents C++ from doing this, but it's so rarely needed (usually only in ultra-high performance low level code), that most C++ code doesn't bother. If you were writing C++ code that really needed this feature, there's a decent chance that it would benefit from hand-tuning with more direct control, causing you to write that particular component in C anyway.
C++ does reduce the need for this strategy a little, since smart pointers (largely) remove the risk of failing to deallocate (as the Postgres docs note, one of the advantages is that it is "more reliable" than per chunk bookkeeping, indicating it reduces the risk of a leak), so C benefits a little more, but both C and C++ benefit from reduced overhead (no per-allocation allocator memory overhead, no paying a deallocation cost for every allocation), so C++ could reap benefits from region based management.
Related
Context: I'm working on a project where a client needs us to use custom dynamic memory allocation instead of allocating objects from the stack. Note that the objects in question have size known during compilation and doesn't even require dynamic allocation. Which makes me wonder,
What are some contexts where custom dynamic memory allocation of objects can be better than allocating objects from the stack? (where size is known during compilation)
An example. If Dog is a class, then instead of just declaring Dog puppy; they want us to do
Dog* puppy = nullptr;
custom_alloc(puppy);
new(puppy) Dog(); // the constructor
// do stuff
puppy->~Dog(); // the destructor
custom_free(puppy)
The real custom_alloc function is not known to us. To make the program run, the given custom_alloc function would be a wrapper of malloc. And custom_free would be a wrapper of free
I do not like this approach and was wondering when this can be actually useful or what they are really trying to solve by doing this.
Possible reasons:
Stack size is limited; while typical thread libraries allocate 1-10 MB for each thread's stack, it's not uncommon for the limit to be set lower for applications where hundreds or thousands of threads are expected to be launched concurrently (e.g. high traffic webservers; Microsoft IIS used to use a 256 KB limit, and only upped it to 512 KB for 64 bit setups).
You may want to keep an object around after the function has returned (without using globals). While NRVO and/or move semantics does mean it's often relatively cheap to return the object by value, when NRVO doesn't apply, copying around a single pointer is cheaper than just about anything else.
Auditing/tracing: They may want to use their custom function for specific types to keep track of memory allocation patterns
Persistent storage: The allocator may be backed by a memory mapped file; for structured data, that file may double as long term storage
Performance: Custom allocators (e.g. Intel's TBB) have been known to dramatically reduce runtime in certain circumstances. This is more a justification for using a custom allocator instead of the default allocator; custom allocators generally won't beat stack storage (except in really niche cases where memory locality might be improved by removing large objects from the stack and putting them in their own dedicated storage).
(Likely a terrible idea) Avoiding exception handling cleanup overhead. If your classes are RAII, then code has to be generated to clean them up along various code paths in case of an exception. Raw pointers don't generate any such code. Of course, if you don't take measures to perform the cleanup on exception yourself, this means memory leaks, but in rare cases (e.g. when you expect the program to exit completely, and you want the OS to handle memory cleanup) this might provide a minor "benefit".
A combination of the above: They may want to be able to swap between a tracing allocator and a performance allocator by linking different runtime libraries to provide custom_alloc
All that said, their approach to do this is pretty awful; requiring manual placement new and destructor invocation is unpleasant (std::unique_ptr/std::shared_ptr could help a bit by providing custom deleter functors that do this work for you, but it's ugly even so). Typically if you need a custom allocator, you'd define appropriate overloads for operator new/operator delete. That way, avoiding stack allocation (for whatever reason) isn't nearly so unpleasant; you just replace logically stack allocated variables with std::unique_ptrs (created via std::make_unique), and your code remains fairly simple.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Stack attributes:
class MyStackClass
{
public:
MyStackClass (int a)
{
myType.create (a); // "create" is an alternative to Type constructor
}
private:
Type myType;
};
Heap attributes:
class MyHeapClass
{
public:
MyHeapClass (int a) { myType = new Type (a); }
~MyHeapClass () { delete myType; }
...
private:
Type *myType;
};
I only use dynamic allocation when it's necessary, for example, dynamic arrays, variables that can don't exist, etc. But I've seen that some many programmers use heap allocation for attributes when it's not really necessary. What's the reason?, I mean, it's more expensive heap allocation than stack allocation. So, when should I use the "heap" way instead of the "stack" way??
Thanks.
Edit:
Assuming that "MyHeapClass" have copy constructor, and operator = to meet the needs.
This is a fundamentally somewhat opinion based answer. However I will bring up a few matters that you should take into account here. In addition the C++ standard gives no mention to the stack or heap, only to variable scopes and lifetimes. Therefore this answer is implementation specific to systems whose C++ implementations actually use typical stacks and heaps.
Size Limitations
The stack is usually substantially smaller than the heap. Often in the range of a few megabytes. The heap on the other hand is usually several gigabytes on modern systems.
If you put more on the stack than it allows your software might crash, or worse begin to act unpredictably. Therefore it is often better to allocate very large objects on the heap.
On the other hand there is substantial overhead to allocating heap objects as you mentioned, though there is the notable advantage that allocation can fail in a language defined way, which you can handle rather than a crash or worse in the case of a stack allocation failure.
Passing Around and Returning
Second we need to bring up the cost of passing objects around. Specifically, heap objects are usually handled by pointer or reference, so passing these around is extremely cheap. The same can be done with stack objects except for the case they must survive for longer than the current scope. In that case they must either be copied (excepting move semantics or RVO), or you will be faced with using an expired stack frame, bad news and undefined behavior.
Variable Length Objects
Finally we need to bring up objects of unpredictable size, like arrays, vectors etc. As far as I know there is not an easy way to allocate an object or runtime known size on the stack in the standard. The non standard alloca allows this but is dangerous in a variety of ways.
Timing
In sections where timing is particularly critical, such as either a real tiem requirement or a tight performance critical loop the overheads of heap allocation are undesirable in multiple ways. Therefore one might lean toward stack allocation more.
However there are various work arounds to this, including preallocating the maximum amount of memory the timing cricial code might use in advance, using a memory allocator with a guaranteed O(1) time complexity (which is likely not useful normally due to slowness and inefficient memory use) or using an object pool or arena allocator to quickly recycle memory old heap objects of the same type used to avoid now allocations.
In Conclusion
These points all sum up to a general rule that objects that are large, or of unpredictable and possibly large size should usually find themselves on the heap, while objects that are small, particularly if they are of a local scope and dont need to be passed around too much should go on the stack.
However this is not a hard and fast rule and there are a multitude of exceptions. A programmer in each case must give thought to the best location of an object.
with C++11 and later, you should almost never use explicit (calling new and delete) heap allocation : it's more dangerous.
Using heap allocation for heap allocation is not a good way programming in C++.
You should (explicitly) use dynamic allocation only when you don't have any other choice.
Dynamic array and variable that may or may not exist require dynamic allocation but C++ standard provide classes for dealing with them easily (vector, unique/shared/weak_ptr and optional with C++17).
Of course, those classes use heap allocation, but they do it safely.
One of the uses of pointer are for non-copiable classes in standard containers, and for polymorphic types in container.
Anyway, you should always wrap resource management (pointer or files ...) in a class (see RAII idiom)
In some languages variables are allocated from the heap, like C# and Java.
The C++ language offers more choices, such as local allocation (what you refer to as stack), heap and global/automatic location.
Java and C# have garbage collection and C++ doesn't. The languages with garbage collection allow a 3rd party task to periodically go through the heap and cleanse (reorder) the memory so that more memory can be used. This task does not exist in C++.
The C++ freedom allows one to declare variables and have them removed after they are not used (such as leaving scope). Variables that need to exist beyond the scope they were declared in, are allocated on the heap (for example, creating variables used by many functions). This eliminates the need to rely on a garbage collector.
On memory constrained systems, such as embedded devices, memory fragmentation is a big concerned. Some systems, such as cell phones, can be power-cycled to defragment the memory. Other systems, such as medical devices and aerospace devices, power cycling is not a preferred method for defragmenting the memory. (Imagine rebooting an airplane's computer system while in flight at a high altitiude.). So the idea is not to use heap memory and use other means. The stack allocation is one of those means.
On some critical timing systems, meeting events is critical. Having a garbage collector run at random intervals means that some of these critical events may be missed. Fragmentation of heap memory is also detrimental in these systems. Thus there needs to be some way of allocating variables without causing defragmentation. The "stack" allocation method helps eliminate this issue.
I'm new to C++ and I'm wondering why I should even bother using new and delete? It can cause problems (memory leaks) and I don't get why I shouldn't just initialize a variable without the new operator. Can someone explain it to me? It's hard to google that specific question.
For historical and efficiency reasons, C++ (and C) memory management is explicit and manual.
Sometimes, you might allocate on the call stack (e.g. by using VLAs or alloca(3)). However, that is not always possible, because
stack size is limited (depending on the platform, to a few kilobytes or a few megabytes).
memory need is not always FIFO or LIFO. It does happen that you need to allocate memory, which would be freed (or becomes useless) much later during execution, in particular because it might be the result of some function (and the caller - or its caller - would release that memory).
You definitely should read about garbage collection and dynamic memory allocation. In some languages (Java, Ocaml, Haskell, Lisp, ....) or systems, a GC is provided, and is in charge of releasing memory of useless (more precisely unreachable) data. Read also about weak references. Notice that most GCs need to scan the call stack for local pointers.
Notice that it is possible, but difficult, to have quite efficient garbage collectors (but usually not in C++). For some programs, Ocaml -with a generational copying GC- is faster than the equivalent C++ code -with explicit memory management.
Managing memory explicitly has the advantage (important in C++) that you don't pay for something you don't need. It has the inconvenience of putting more burden on the programmer.
In C or C++ you might sometimes consider using the Boehm's conservative garbage collector. With C++ you might sometimes need to use your own allocator, instead of the default std::allocator. Read also about smart pointers, reference counting, std::shared_ptr, std::unique_ptr, std::weak_ptr, and the RAII idiom, and the rule of three (in C++, becoming the rule of 5). The recent wisdom is to avoid explicit new and delete (e.g. by using standard containers and smart pointers).
Be aware that the most difficult situation in managing memory are arbitrary, perhaps circular, graphs (of reference).
On Linux and some other systems, valgrind is a useful tool to hunt memory leaks.
The alternative, allocating on the stack, will cause you trouble as stack sizes are often limited to Mb magnitudes and you'll get lots of value copies. You'll also have problems sharing stack-allocated data between function calls.
There are alternatives: using std::shared_ptr (C++11 onwards) will do the delete for you once the shared pointer is no longer being used. A technique referred to by the hideous acronym RAII is exploited by the shared pointer implementation. I mention it explicitly since most resource cleanup idioms are RAII-based. You can also make use of the comprehensive data structures available in the C++ Standard Template Library which eliminate the need to get your hands too dirty with explicit memory management.
But formally, every new must be balanced with a delete. Similarly for new[] and delete[].
Indeed in many cases new and delete are not needed, you can just use standard containers instead and leaving to them the allocation/deallocation management.
One of the reasons for which you may need to use allocation explicitly is for objects where the identity is important (i.e. they are not just values that can be copied around).
For example if you have a gui "window" object then making copies probably doesn't make sense and thus you're more or less ruling out all standard containers (they're designed for objects that can be copied and assigned). In this case if the object needs to survive the function that creates it probably the simplest solution is to just allocate explicitly it on the heap, possibly using a smart pointer to avoid leaks or use-after-delete.
In other cases it may be important to avoid copies not because they're illegal, but just not very efficient (big objects) and explicitly handling the instance lifetime may be a better (faster) solution.
Another case where explicit allocation/deallocation may be the best option are complex data structures that cannot be represented by the standard library (for example a tree in which each node is also part of a doubly-linked list).
Modern C++ styles often frown on explicit calls to new and delete outside of specialized resource management code.
This is not because the stack/automatic storage is sufficient, but rather because RAII smart resource owners (be they containers, shared pointers, or something else) make almost all direct memory wrangling unnessecary. And as the problem of memory management is often error prone, this makes your code more robust, easier to read, and sometimes faster (as the fancy resource owners can use techniques you might not bother with everywhere).
This is exemplified by the rule of zero: write no destructor, copy/move assign, copy/move constructor. Store state in smart storage, and have it handle it for you.
None of the above applies when you yourself are writing smart memory owning classes. This is a rare thing to need to do, however. It also requires C++14 (for make_unique) to get rid of the penultimate excuse to call new.
Now, the free store is still used, just not directly, under the above style. The free store (aka heap) is needed because automatic storage (aka the stack) only supports really simple object lifetime rules (scope based, compile time deterministic size and count, FILO order). As runtime sized and counted data is common, and object lifetime is often not that simple, the free store is used by most programs. Sometimes copying an object around on the stack is enough to make the simple lifetime less of a problem, but at other times identity is important.
The final reason is stack overflow. On some C++ implementations the stack/automatic storage is seriously constrained in size. What more is that there is rarely if ever a reliable failure mode when you put to much stuff in it. By storing large data on the free store, we can reduce the chance the stack will overflow.
First, if you don't need dynamic allocation, don't use it.
The most frequent reason for needing dynamic allocation is that
the object will have a lifetime which is determined by the
program logic rather than lexical scope. The new and
delete operators are designed to support explicitly managed
lifetimes.
Another common reason is that the size or structure of the
"object" is determined at runtime. For simple cases (arrays,
etc.) there are standard classes (std::vector) which will
handle this for you, but for more complicated structures (e.g.
graphs and trees), you'll have to do this yourself. (The usual
technique here is to create a class representing the graph or
tree, and have it manage the memory.)
And there is the case where the object must be polymorphic, and
the actual type won't be known until runtime. (There are some
tricky ways of handling this without dynamic allocation in the
simplest cases, but in general, you'll need dynamic allocation.)
In this case, std::unique_ptr might be appropriate to handle
the delete, or if the object must be shared, std::shared_ptr
(although usually, objects which must be shared fall into the
first category, above, and so smart pointers aren't
appropriate).
There are probably other reasons as well, but these are the
three that I've encountered the most often.
Only on simple programs you can know beforehand how much memory you'd use. In general you can not foresee how much memory you'd use.
However with modern C++11 you generally rely on standard libraries like vector and map for memory allocation, and the use of smart pointers helps you avoid memory leaks, so you don't really need to use new and delete explicitly by hand.
When you are using New then your object stores in Heap, and it remains there until you don't manually delete it. but in the case without using new your object goes in Stack and it destroys automatically when it goes out of scope.
Stack is set to a fix size, so if there is no any block for assign a new object then Stack Overflow occurs. This often happens when a lot of nested functions are being called, or if there is an infinite recursive call. If the current size of the heap is too small to accommodate new memory, then more memory can be added to the heap by the operating system.
Another reason may be if you are explicitly calling an external library or API with a C-style interface. Setting up a callback in such cases often means context data must be supplied and returned in the callback, and such an interface usually provides only a 'simple' void* or int*. Allocating an object or struct with new is appropriate for such actions, (you can delete it later in the callback, should you need to).
I need some clarification about c++ memory management and MISRA guidelines..
I have to implement one program that it's MISRA compatible so I have to respect a important rule: is not possible to use 'new' operator (dynamic memory heap).
In this case, for any custom object, I must use static allocation:
For example:
I have my class Student with a constructor Student(int age).
Whenever I have to instantiate a Student object I must do it this way:
int theAge = 18;
Student exampleOfStudent(theAge);
This creates an Student object exampleOfStudent.
In this way I do not to have to worry about I do not use destructors.
Is this correct all this?
Are there other ways to use static memory management?
Can I use in the same way std::vector or other data structure?
Can I add, for example, a Student instance (that I created as Student exampleOfStudent(theAge)) into a std::vector.
Student exampleOfStudent(theAge); is an automatic variable, not static.
As far as I remember, MISRA rules disallow all forms of dynamic memory. This includes both malloc and new and std::vector (with the default allocator).
You are left with only automatic variables and static variables.
If your system has a limited amount of RAM you don't want to use dynamic memory because of the risk you will ask for more memory than is available. Heap fragmentation is also an issue. This prevents you from writing provably correct code. If you use variables with automatic or static storage a static analysis application can, for instance, output the maximum amount of memory your application will use. This number you can check against your system RAM.
The idea behind the rule is not that malloc and new, specifically, are unsafe, but that memory allocation is (usually) a lazy workaround for not understanding, or managing, the memory requirements of your program.
pre-allocating your calculated maximum input, and trapping overruns
providing a packet, stream, or other line-oriented means of managing input
use of an alternative pre-allocated data structure to manage non-uniform elements
Particularly in the context of a small, non-MMU, embedded system that lack of design depth frequently leads to an unstable system, that crashes outright in those odd, "corner case" exceptions. Small memory, short stack, is a system killer.
A few, of many, strategies that avoid the assumption that you do not have infinite memory, or even much memory in that inexpensive, embedded system - and force you to deal with the faults that might be important in your application.
Don't write your own malloc.
For MISRA compliance, placement-new is not a problem, as there is no dynamic allocation happening.
A library could be written (like an STL allocator) in such a way as to reference a statically allocated memory region as it's memory pool for such a purpose.
Advantages: deterministic, fast.
Disadvantages: memory inefficient.
A favorable trade off for deterministic real-time systems.
All needed RAM has to be there at program startup, or the program won't run.
If the program starts, it's unaffected by available heap size, fragmentation etc..
Writing ones own allocator can be complex and out-of-memory conditions (static memory pool size is fixed after all) still have to be dealt with.
I once wrote a library that had to comply to the MISRA rules. I needed dynamic memory as well, so I came up with a trick:
My lib was written in C, but my trick may work for you.
Part of the header-file looked like this:
/* declare two function pointers compatible to malloc and free: */
typedef void * (*allocatorFunc)(size_t size);
typedef void (*freeFunc) (void * data);
/* and let the library user pass them during lib-init: */
int library_init (allocatorFunc allocator, freeFunc deallocator);
Inside the library I never called malloc/free directly. I always used the supplied function-pointers. So I delegated the problem how to the dynamic memory allocation should look like to someone else.
The customer actually liked this solution. He was aware of the fact that my library would not work without dynamic memory allocation and it gave him freedom to implement his own memory scheme using preallocated pools or whatnot.
In C++ you can do the same, just use the malloc function and do the object creation using placement new.
I'm implementing a compacting garbage collector for my own personal use in C++0x, and I've got a question. Obviously the mechanics of the collector depend upon moving objects, and I've been wondering how to implement this in terms of the smart pointer types that point to it. I've been thinking about either pointer-to-pointer in the pointer type itself, or, the collector maintains a list of pointers that point to each object so that they can be modified, removing the need for a double de-ref when accessing the pointer but adding some extra overhead during collection and additional memory overhead. What's the best way to go here?
Edit: My primary concern is for speedy allocation and access. I'm not concerned with particularly efficient collections or other maintenance, because that's not really what the GC is intended for.
There's nothing straight forward about grafting on extra GC to C++, let alone a compacting algorithm. It isn't clear exactly what you're trying to do and how it will interact with the rest of the C++ code.
I have actually written a gc in C++ which works with existing C++ code, and it had a compactor at one stage (though I dropped it because it was too slow). But there are many nasty semantic problems. I mentioned to Bjarne only a few weeks ago that C++ lacks the operator required to do it properly and the situation is that it is unlikely to ever exist because it has limited utility..
What you actually need is a "re-addres-me" operator. What happens is that you do not actually move objects around. You just use mmap to change the object address. This is much faster, and, in effect, it is using the VM features to provide handles.
Without this facility you have to have a way to perform an overlapping move of an object, which you cannot do in C++ efficiently: you'd have to move to a temporary first. In C, it is much easier, you can use memmove. At some stage all the pointers to or into the moved objects have to be adjusted.
Using handles does not solve this problem, it just reduces the problem from arbitrary sized objects to constant sized ones: these are easier to manage in an array, but the same problem exists: you have to manage the storage. If you remove lots of handle from the array randomly .. you still have a problem with fragmentation.
So don't bother with handles, they don't work.
This is what I did in Felix: you call new(shape, collector) T(args). Here the shape is a descriptor of the type, including a list of offsets which contain (GC) pointers, and the address of a routine to finalise the object (by default, it calls the destructor).
It also contains a flag saying if the object can be moved with memmove. If the object is big or immobile, it is allocated by malloc. If the object is small and mobile, it is allocated in an arena, provided there is space in the arena.
The arena is compacted by moving all the objects in it, and using the shape information to globally adjust all the pointers to or into these objects. Compaction can be done incrementally.
The downside for a C++ programmer is the need to construct a correct shape object to pass. This doesn't bother me because I'm implementing a language which can generate the shape information automatically.
Now: the key point is: to do compaction, you must use a precise collector. Compaction cannot work with a conservative collector. This is very important. It is fine to allow some leakage if you see an value that looks like a pointer but happens to be an integer: some object won't be collected, but this is usually no big deal. But for compaction you have to adjust the pointers but you'd better not change that integer: so you have to know for sure when something is a pointer, so your collector has to be precise: the shape must be known.
In Ocaml this is relatively simple: everything is either a pointer or integer and the low bit is used at run time to tell. Objects pointed at have a code telling the type, and there are only a few types: either a scalar (don't scan it) or an aggregate (scan it, it only contains integers or pointers).
This is a pretty straight-forward question so here's a straight-forward answer:
Mark-and-sweep (and occasionally mark-and-compact to avoid heap fragmentation) is the fastest when it comes to allocation and access (avoiding double de-refs). It's also very easy to implement. Since you're not worried about collection performance impact (mark-and-sweep tends to freeze up the process in a nondeterministically), this should be the way to go.
Implementation details found at:
http://www.brpreiss.com/books/opus5/html/page424.html#secgarbagemarksweep
http://www.brpreiss.com/books/opus5/html/page428.html
A nursery generation will give you the best possible allocation performance because it is just a pointer bump.
You could implement pointer updates without using double indirection by using techniques like a shadow stack but this will be slow and very error prone if you're writing this C++ code by hand.