Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to allocate memory properly in C++?



  • In C/C++ there are three ways to allocate memory for objects:

    • Dynamic allocation in stack
    • Dynamic allocation in heap
    • Static allocation in bss/data

    Each of these areas has some benefits and some drawbacks, like allocation in stack is faster than heap but it is limited.

    Now my question is when should I use which area in what condition to instantiate objects?
    As objects of QApplication and QMainWindow are the main objects of application and their existance is a must for lifetime of programme, isn't it better to instantiate them inside bss/data to decrease stack memory usage and prevent stack overflow?


  • Lifetime Qt Champion

    Hi

    One has to take into account the Qt ownership system
    https://doc.qt.io/qt-5/objecttrees.html

    so while this is perfectly legal

    class MainWindow : QMainWindow {
    QPushbutton Button;
    }

    you might run into issues if you the button put in a layout as then both Qt and the normal c++ will try to delete it.

    For other non-visual classes, it makes perfect sense to have them as non pointer members.

    for static allocation (using static keyword) then be aware that no QObject may exist before the QApplication created in main.

    So for anything that makes up a hierarchy , you will often use heap.
    For local use / you will often use stack.
    Like

    Dialog myDialog;
    myDialog.exec();

    no reason to put myDialog on the heap as exec is blocking so it wont run out of scope.

    So the right way depends on the use case and if visual.


  • Moderators

    Now my question is when should I use which area in what condition to instantiate objects?

    If you expect an algorithm like if(something) use stack else if (something else) use heap else use static storage then there's no such nard rules. The answer is "it depends" and the amount of factors it depends on is enough to write several books on the topic (and there indeed are). I know this might not sound like satisfactory answer if you're new to this but to help you there are some rough guidelines. Keep in mind though that it's all they are and there are plenty of exceptions and reasons not to follow them.

    Anyway, for QObjects it's pretty much what @mrjj said:

    • Qt manages parent/child objects lifetime, so anything that is meant to be deleted by the parent needs to go on the heap
    • Anything else (which is not much - root objects, locals, scope bound stuff) goes on the stack.
    • Never create static QObjects. The order of initialization/destruction for those is unspecified and that doesn't work with Qt. Some classes might work, others might seem to work but don't, many will straight up crash your app.

    As for non Qt, static storage is for data and uninitialized stuff. While there's a lot you can use it for don't put every little thing you can in there because they are bad for cache locality, have ordering issues, especially when mixing with dynamic libraries and such. Stuffing a lot in there also increases your executable image size, which can be an issue too in some environments. Keep it simple - use it for constants, arrays of data, string literals etc.
    Stack is your bread and butter. It's the memory part managed entirely by your app, so it's the fastest. Use it as much as you can. The reasons you can't include - the thing is too big (stack is small), its lifetime doesn't match any reasonable scope or the API you use requires you not to.
    Heap is the warehouse. It's big but it takes time to allocate and free space in it. Use it to store a lot of stuff or when you can't use stack. Using heap is slower because allocation/deallocation goes through the OS, and, since it's a lot of space, it has worse cache locality than stack.



  • How can I moderate stack to prevent stack overflow?
    How can I evaluate my programme to measure maximum stack usage during runtime?


  • Moderators

    How can I moderate stack to prevent stack overflow?

    At runtime? the easiest way is just don't put stuff there. If you're worrying about overflowing the stack with normal usage you probably already put too much there. Most objects have sizes counted in bytes, sometimes dozens or hundreds. For example if you have a 30 function deep call stack and each function has, say 10 variables, being, for example, 8 bytes in size, you're looking at a stack usage below 3KB. There's plenty of room on the stack for usual tasks and it's not that easy to overflow. Just don't go crazy and put large arrays there.
    I mean you could put some variables on the stack, take their addresses and do some arithmetic to calculate how much memory from the stack you're consuming. Your particular OS API might also have some functions to tell you the stack base address etc. Most compilers also let you override the default stack size via some switches, but again, that's something you would rarely do, and from what I've seen it's usually to reduce the default stack size in a massive multithreaded apps to reduce overall app memory footprint.

    Are you asking because you do heave a stack overflow problem or just in case? Because it's not something you worry too much about in day to day programming.

    How can I evaluate my programme to measure maximum stack usage during runtime?

    There are some profiling tools and APIs for that. Linux has getrusage() which I think could tell you something. There are tools like Intel VTune which profiles all sorts of memory usage aspects, but again, that's not something you usually do, only in cases where you have an actual stack overflow problems.



  • As a general rule of thumb I would say prefer to put your variables on the stack. Reason number one for this is object lifetime: it is managed automatically when putting something on the stack. Sure, now we have smart pointers which also help manage lifetimes. But a local variable has restricted lifetime within the local scope (which a shared_ptr might not) which makes reasoning about it quite easy. The second reason is speed. As already mentioned allocation/deallocation on the heap is comparatively slow. Especially for short object lifetime with little work in between it is not worth the hassle (in 99% of the time you wouldn't allocate an int or a double on the heap).

    Large objects, however, should live on the heap. Most of the time this is only the case with arrays of data. These usually have a number of entries not known at compile time. There is no way (yet) in C++ to dynamically allocate arrays on the heap stack. There is, however, this functionality in C. Still, there are few case where you should do that, as you correctly noted that the stack is limited in size. Speaking of C++, most of the time you should use a vector class (like std::vector) instead of plain arrays. The vector object can live on the stack (managing lifetime correctly) whereas internally its array is allocated on the heap. Together with move semantics and guaranteed return value optimization (since C++17???) you can even return a std::vector "allocated" on the stack (it's quite cheap independent of the number of elements it stores).

    BTW, usually arrays and vectors tend to be a little larger. If you just do a tiny amount of work on every element of the array/vector the cost for allocation/deallocation of the array on the heap quickly amortizes.

    Extended object lifetime (continuing after leaving a function) is also a reason heap storage. For non-moveable objects it is advisable if a copy is expensive, for non-copyable objects it is even the only possible way (if they are also non-moveable).

    As Chris Kawa already mentioned don't try to forceably use static storage. I will not go into detail here as it really shouldn't be your common case.

    Still, there is more to the discussion. When talking about object oriented programming you will soon encounter runtime polymorphism. This is the case with virtual methods inside classes. For simple case you could still allocate them on the stack and pass a reference or pointer to the base class to other functions. As soon as you want to store objects of an inheritance hierarchy in an array or vector the common approach is to use pointers (or even smart pointers). You simply cannot store the object directly inside a polymorphic container. That last sentence is only partially true as with std::variant there is a way to store objects directly inside the container. If it actually makes sense depends on the object sizes (if they differ largely) and the type of operations you do on the container (erasing something in the middle of a std::vector or sorting is expensive on objects because of copying – pointers to objects are a lot cheaper in these cases).

    As Chris Kawa already mentioned classes derived from QObject usually manage object lifetime differently. As long as an object has a parent its parent will call delete on the object when the parent is destroyed. This implies allocation on the heap. There is no trick around that. QObjects with a null-pointer parent can, in general, be put on the stack. With Qt Widgets you should note that putting a widget into a layout reparents the object.

    I work on large code bases where speed is paramount. So, I follow my own advice and allocate as much as possible on the stack. I can only confirm what Chris Kawa says that the stack size has never been a problem. I have run out of stack in a few cases. Number one reason is bugs. Ocassionally, by accident I have introduced infinite recursions. This can easily happen when connecting signals of widgets to slots that manipulate widgets themselves. A stack overflow usually occurs shortly after passing a stack depth of a 1000 function calls. Sometimes, a recursive function looks like the best tool (e.g. traversing a tree). So, you should know that there is a limit. Recursion can always be rewritten by using a loop. This is your way out if you run into trouble. Some compilers, e.g. GCC, can recognize tail recursion and optimize it to be similar to a loop (avoiding allocation of deep stacks). The only second reason for a stack overflow I ever encountered was found in using Fortran. There it is quite easy to allocate arrays on the stack. When reaching several million entries these large arrays are a problem. It will not be a problem if you follow the advice from above to allocate arrays on the heap or use vector classes directly.


  • Lifetime Qt Champion

    @SimonSchroeder said in How to allocate memory properly in C++?:

    There is no way (yet) in C++ to dynamically allocate arrays on the heap

    I think you meant to write:

    There is no way (yet) in C++ to dynamically allocate arrays on the stack.

    Regards



  • @aha_1980
    Use alloca() ;-) [Not seriously recommended, and not a part of the C++ spec.]



  • @Chris-Kawa said in How to allocate memory properly in C++?:

    Are you asking because you do heave a stack overflow problem or just in case?

    No. I care about the quality of my code and optimization of generated binary a lot. So I wanted to know more about handling memory management in C++/Qt


  • Lifetime Qt Champion

    Hi
    Just a note about " dynamically allocate arrays on the stack."

    "draft C++14 now allows stack-based arrays to have a size determined at run time:"

    void f(std::size_t n)
    {
    int a[n];
    ...
    }

    https://isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meeting

    So that didnt make to the release ?

    Or do we generally mean more dynamic?



  • @mrjj
    E.g. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0785r0.html [from 2017]

    Both facilities were removed from C++14 and instead put into a separate Technical Specification in Chicago, September 2013

    ? :)


  • Lifetime Qt Champion

    @JonB
    Ok so we didnt get that. thx :)


Log in to reply