in my C json parser I allocate memory only once, and it is while reading the files content, so then everything else that might need to access it's contents, like, we have a variable `"kind": "Person"`, instead of doing a copy of that memory, I just do a string view. so basically I just store the pointer to the start of the section I need from the string, and the length of it. I didn't did this in my first prototype, and for tokens I allocated 1024 bytes for each lexeme, but I figured out - mhh, maybe having a fixed limit on that is kinda stupid, and allocating 1024 extra bytes for each token is a bit of an over kill - so by doing a string view, I allocate 1 pointer + 1 size_t, so we just allocate 12 bytes in total :D ( this depends on the size of size_t in your computer and the size of a pointer, but it is still way better than allocating 1024 bytes ) so remember guys, never allocate memory if you don't need to, neither in the stack or the heap, just allocate as much memory as strictly needed for your program to work!
if you really need a lot of objects of a type and have to use the heap, i would just consider making a whole class to handle the memory alocation and deletion there and it would act just like the regular object, it's easier imo edit: here's how the class would look like: template class HeapObject { protected: T *ob; public: HeapObject() {ob = new T;} ~HeapObject() {delete ob;} void operator=(T a) {*ob = a;} /// other methods, i can't be bothered to give more examples, it's just here to show that you can do it T operator*() {return *ob;} };
Since you are using containers to store your entities, your entities are on the heap (cuz the elements of a container are allocated on the heap), so you are not using stack memory at all. Besides this, nice advice!
The only overhead from a heap is the allocation itself. If you allocate the objects as an array you still get all the benefits of cache locality and fast memory access of the stack. And if the size never changes then you don't need to reallocate so using this memory is essentially free.
@@lowlevelgamedev9330 but using std::vector literally comes with all of the caveats that using new and std::unique_ptr do, just wrapped up in a more convenient container. For example, you need to be ready for std::bad_alloc if you're considering that the other allocations can fail. Generally, I really agree with your overall message. Stack allocation is always better than heap allocation, unless you actually need to heap allocate. Using std::vector and friends is an awesome way to manage dynamically allocated memory, too. However, consider using smart pointers instead of the five raw pointers you have (unless they aren't pointers to data that is owned by the object). There's essentially no weight at all for a std::unique_ptr and they are pretty easy to use, not to mention guaranteeing freeing allocated memory in the face of code refactoring and exceptions. It is also good to add new tools to your toolkit!
4:21 you can probably get rid of the call to `new` in you filedata var. Make use of the appropriate overloaded constructor to create an std::vector initialized with X elements of unsigned char zero initialized aka std::vector fileData(fileSize). And the use it like this "file.read(fileData.data(), fileData.size());". This takes advantage of the fact that a vector is guaranteed to use contiguous memory to store its elements.
To be honest, avoiding the heap for any programming at all is a great rule of thumb. The edge cases where you'd actually need the heap are pretty few and far in between.
I wouldn't call them "edge cases". When you want to load a large file like an image or audio, you need to store it on the heap because the stack isn't big enough. You also need the heap to store data structures like trees and graphs. Thankfully, the STL containers reduce the need for these kind of tasks but there are times when you need them.
@@bigjango13 I can't tell because I still didn't learn how to use mmap. I've been willing to do so for a long time now though. What I can say is that malloc is implemented using mmap on modern systems (it used to be implemented using brk and sbrk) so I guess they just do the same thing.
2:21 don't use raw arrays. Use std::array instead. They encode the size in them. Also the compiler would be able to make it as if you used a raw array. Aka zero overhead.
@@zanagi No, it's normal vector, but with different allocator (std::vector takes an allocator as second template argument). It basically allows you to preallocate memory block on stack and then suballocate from it. Allocations are fast and close in memory.
I love the pmr idea, but each time I use them I end up wondering how did I write something so ugly. I think c++26 will have a stack vector. Maybe I'll be able to use it at work by 2035.
Almost every component of my ECS is living on the stack 😅 .. who needs names or endless strings, user input is limited, e.g. player name is max. 24 characters. Expect for textures, sound and other assets ... .. there are living in the resource loader, loaded once, freed at the end. The only things that are RAII, are my Scenes/Levels, with init(), enter(), exit() ... Methods ... The Scene itself lives in a "Context" variable (held in the Scene manager). When switching scenes, the scene either gets "disabled" or uninit/init. Everything else lives in the ECS registry/world, the scenes queries the components there need.
RAII is the stack though, like anything you create on the stack is managed through RAII, unless its a primitive data type, it will call the constructor on initialization and destructor on leaving the scope, that is RAII.
@@Spartan322 I assume these are defined in some top level function (maybe even in main) and passed around to other functions as pointers/references so RAII will not trigger as often
@@maksymiliank5135 Doesn't matter, RAII is stack allocation and that's still the stack, the object itself may allocate on the heap, like smart pointers or vector, but all memory behavior of the object itself is on the stack and anything that doesn't allocate on the heap is stack allocated and also performs RAII.
I wrote a memory management class in C++ which handles all memory allocations and deallocations with tracking and logging support. The same can be done with a struct if using C.
0:48 Step 1: Don’t allocate memory when you don’t need to 1:20 - Cool trick 1:37 - Cons and pros of allocating memory on the heap 1:54 - When to allocate on the stack or on the heap 3:46 Step 2: If you need to allocate memory, try to use containers 4:35 Step 3: Create specialized systems to manage memory for more difficult things
I once worked for a 2d pixel retro-rpg where i did the C++ part and since we are not in the 90ies any more and devices have more than 2 megabytes of RAM we could ignore a lot of the memory management by loading a lot of stuff at the start.
it's also pretty easy to have memory leaks in languages with garbage collection. in java it's pretty easy to have memory leaks because you didn't dispose of an object in a specific way and then the garbage collector thinks it's still in use. and when you try to debug those issues it gets really ugly because it's hard to monitor what the garbage collector actually does. I always wish java had at least an option for doing manual memory management under certain circumstances
@@lowlevelgamedev9330 ofc you can in any language with gc btw not just java. it just reduces the risk of memory leaks. in special human error by the programer causes it in gc languages, in special when they don't know how the gc behaves in the language their working in.
It boggles my mind how many people keep insisting that you need to often deal with memory management in C++, like in almost every case I've observed, if you're managing memory directly, you should be using a container of some sort, or you should be using the stack. There are even only a limited set of reasons to deal with a raw pointer directly without managing it. Ownership semantics with smart pointers even support dynamic arrays now, so why would you never not use them? Probably because people don't bother the learn C++ and just keep doing things the C way.
@@bo4-x2n You don't know what a garbage collector is do you? C++ ownership semantics don't need a GC, you don't need a GC if you use RAII, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
@@bo4-x2n You need to learn what a GC is. C++ ownership semantics don't need a GC, RAII invalidates the need for a GC, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
@@bo4-x2n You need to learn what a GC is. C++ semantics don't need a GC, RAII invalidates the need for it, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
@@lowlevelgamedev9330 shared pointers cause more problems than they solve in MOST cases so that's good. Unique pointers, however, are an improvement on using the new keyword in every single way so definitely use make_unique instead of using the new keyword if you end up needing to manage your own memory allocation, like when storing large objects on the heap.
And does that help you with memory fragmentation in any way? no? then what do you earn from this? the point of this video is for using stack allocated variables or preallocated static buffers for data. The more static allocations you do, the less you will need to dynamically allocate stuff, the less of a performance hit for your program (less fragmentation and less time wasted waiting for the OS to reserve a chunk of memory for you...). If you need to dynamically allocate, then do so in buffers or large chunks so that you don't have to allocate every single instance one by one individually. It's that simple. Using make_shared and make_unique does not save you from those problems. First, you should try to understand what the heap even is and how it works...
I wrote a renderer with C++/Vulkan for 2D cad stuff and even in 2-3k lines of code I use new all of 3 times to allocate some command pool objects. Don't need new that often.
yes and no, containers use the RAII way of habdeling memory. The constructor (or setters) allocate the memory, and the destructor ensures the memory is deleted, even if an exception is thrown. the std can throw exceptions (unless you explicitely ask it not to), same goes for most c++ libraries.
people think there is a limit to the stack size there isn't the at least on windows the exe NT headers contain a 64 bit slot for the stack size needed by the program
Allocating on the heap is fine. What matters is whether you can predict how much memory you'll need. If you can't, then you won't be able to manage your memory because you don't have a good concept of what it's doing. If you can, then it's simple to just free it all at once way later when you're done with it. As long as you're not interleaving tons of mallocs and frees you won't get into trouble, and that's to say nothing of what's possible when you realize malloc is garbage and you can easily do syscalls yourself. This is the true meaning "of don't allocate more than you need to", advice that's been misconstrued greatly over the years.
I wish the entirety of the r/Cplusplus subreddit would watch this video and stop for one post crying about pointers and memory unsafety. Computer memory is NOT that complex. You allocate bytes. Write shit into them. And then deallocate them. You can do 3 things with memory. If you make it more complicated than that, that's on you.
I am on windows, tho fortunatelly I dom't really need it. Also I don't think it can help with gpu memory :(( I usually have to deal with that more often
@@lowlevelgamedev9330 Ooof Windows, my condolences. Switching over to linux made my life as Dev so much better. Anways I recommend you add some debug logic that keeps track of allocated vram, maybe add it to a debug overlay or something along the lines.
How do you handle id invalidation? If you were to create a large complex game, you can't have all rresources loaded from the beggining, and the int index variable might point to an incorrect resource when the resource manager has to delete elements
good questoon, I wod store them in an unordered map, and if an entity is deleted, it just will dissapear from the map, so I can just check if the entry is in the map to know if it exists
What would you then suggest when you have a situation where class A has a member B, but which it doesn't want to initalize straight on A construction, as perhaps it has to do something else first. One could then just let B have an empty constructor and use an Init method but this then goes against RAII, so it seems to me like the only two options in this case is break RAII or use a unique_ptr
break the raii principle lol, I use an init method all the time, and I have a deinit method. Besides std vector and other containers I don't have any raii in my code. For many cases its very convinient to write code like that.
Basically, if you need to deal with memory allocation: 1. Thou shalt call one 'delete' for every 'new'. 2. Separate memory management tasks from other tasks. 3. The "big 3" for a class with memory allocation to avoid crashing: copy constructor and assignment operator for deep copies, and destructor for cleanup.
that is another story, and they crash because that is a mechanism made by the cpp standard library, to help you not forget to either detatch them or join them. If you make them using windows api I don't think it will crash
@@lowlevelgamedev9330 it's only really problematic if you still treat it as a heap allocation since it's always going to get popped at the end of the scope, so just don't do dumb stuff like returning the pointer or passing it around to other threads and it's pretty much fine to use, it's perfect for small stuff like the opengl shader compilation message
Anyone have a link to an explainer on why heap allocated memory may be accessed slower? I thought access times after allocation/the first access would be just as fast as stack allocated memory.
The stack is on the hot code path so more likely to be in the cache, also the heap can be fragmented resulting in non contiguious memory which inhibits prefetching to the cache, and multiple layers of indirection like vector of vectors will makes this much much worse. If you are interesting data oriented design is what you need to look at.
6:43 The index also gets invalidated, so i dont really see the benefit of this tip. Sure on most cases you'll save 4 bytes on 64bit cause the pointer is 8bytes and your int textureIndex would be enough for any game and is only 4. Apart from that, you're passing in the texturemanager and then do a read with the index. That still boils down to reading the underlying pointer since your std::map/unordered_map is heap allocated. So it my mind that is the same speed, maybe even slower. Correct me if im wrong though.
so for the first thing, for example there are 5 players each one with a skin, and they can change. The index won't be invalidated, there are always 5 players, the skin can. That was the case that I was thinking about. If the index can get invalidated you would make sure to check for that of chourse. Also for the second thing I was thinking about sending the gpu id directly, like sending the opengl id, but it doesn't matter, the speed is the same roughly but I mentioned that it can be even faster to make my point very clear.In practice it boils down to how the compiler optimizes it and it is the case that for this thing it can be slightly faster or slower but very small difference
2:50 es mejor usar std::make_unique si no vas a inicializar el std::unique_ptr con un destructor personalizado es mejor porque: - Sigue la regla de evitar el uso de "new" porque actualmente hay mejores opciones para manejar la memoria - "auto p = std::make_unique()" es más corto y conciso que "std::unique_ptr p(new Player{}) - std::unique_ptr está implementado para ser seguro ante las excepciones
well you know all the schools do that but you don't have to do the same in your code 💪💪 their example is just a simple didactic thing, I don't even use ecs in my games lol
So this is why people say C++ isn't memory safe. I never understood this. I almost never use new. Like ever. It feels like reinventing the wheel, so I only do it when absolutely needed.
"So this is why people say C++ isn't memory safe." People say that cause the language it self is not memory-safe and sadly many of the old folks start by first teaching their half-knowledge of C and then their abysmal pre C++98 style.
@@LokiScarletWasHere There are many languages where simple memory access it checked - there is nothing in C++ preventing you from creating an array of 2 elements and then writing to the 1235235 index. You can create a pointer to any arbitrary memorylocation and write to it. That is what i means with the language is not memory safe.
@@ABaumstumpf Again, no language is safe. Some are just more restrictive than others. You just have to get a bit more creative with more restrictive languages if you want to make something that'll compile but segfault. Safety does not exist outside the programmer and there is no free lunch. Even the choice of language, how many training wheels the programmer rides with, is a decision by the programmer, or a decision made for the programmer if the code already existed before they entered the project.
When i did texture manager for my little doom like engine because of memory leaks everyone called me stupid. But look at me now i was stupid but at least solution was smart.
lol that's how top gamedevs do it in game dev. If you look at top gamedev programmers that's what they talk about and they don't use unity they make unity :))
Okay, you touched on a subject I've been wanting to know about for long lol I have a question tho, I remember once having a vector of entities, the vector was getting passed around quite a lot, you see I have this preference of passing things as parameters instead of making them accessible globally or make a system for them (Unless the need arises) However, I had a big problem where the entities vector was getting corrupted somehow? I don't really understand the problem myself but I remember it had an addressing issue, it got fixed when I allocated the entities on the heap So my question: if this is wrong, how wrong is it? and how can I potentially fix it?
You either used the vector wrong or one of the functions you passed it in mutate it accidentally. Either pass it as a const reference (no mutation possible) or as a reference (mutation possible).
@@sledgex9 I am sure I was not mutating, the vectors got passed as a "const &" since it was large The issue is that i noticed is that the entities within had different addresses for some reason, which was very strange to me
@@nazihboudaakkar8117 Unfortunately this kind of problem can't be diagnosed via a comment. All I can say is that vectors should work with no problems. Potentially a misuse was happening somewhere. C++ has many pitfalls you can fall into if you aren't experienced, unfortunately.
If you take a pointer to an item in a vector and then you add/remove things from the vector, it will invalidate the pointer because under the hood reallocation happened.
@@swansonair As I said earlier, I was passing the vector around as a const reference precisely to avoid that and to avoid needlessly copying the contents The issue got solved the moment i changed the entities to be on the heap, I clearly remember not touching anything (besides refactoring things to use the unique_ptr) and It felt sooo awkward But yeah, it was probably a bug on my part
about allocating on the heap, inst it useful for class dependency management? like, hear me out... usually i have a codebase that only uses makefiles and very frequent OOP (don't ask why, i just don't have patience to learn CMake), and by doing so, i frequently encounter problems with changing a certain class to add a new member causing me to recompile the entire project only to make the dependant classes work accordingly, because if i don't, i can start modifying data that i shouldn't be or just cause some really "undebuggable" issues. so my usual solution is to abstract each member with a getter and a setter function, so i don't have to worry about using the . or the -> operator in raw members and alignment, since it will be dealt with when compiling the single c++ file. however i do understand where you come from... nice tips!
@@lowlevelgamedev9330 if i use the stack, the size for structs and classes will be fixed, like: struct A {}; struct B { A a; int bar; }; So, ignoring the class optimization thing that some compilers do, and considering that A and B are on different headers and .cpp files respectively, if I change A to this: struct A { int foo; }; For the .cpp file of B, it still thinks that A has not changed, but A has changed, so A.foo becames the same as B.bar, making some really weird bugs and eventually leading to UB. My fix is to defined the heap allocator for each class inside the .cpp files and wrap foo and bar with getters and setters
@@daemoncluster it’s what that style of programming is called. It’ll claim to be c++ but the programmer will lean more towards the C side of things and only use a small percentage of features from c++ (i.e. the classes)
@@flyingsl0ths Yeah, because C++ is a overly complicated and redundant mess. Even the creators and maintainers can't keep it straight. People think all C is missing is support for classes and automated memory management. C++ went above, beyond, and into completely esoteric territory with features like meta-programming, generics, etc. C is not easy, but it doesn't have the mental overhead that C++ tempts you with. Regardless, C with classes just ends up being C++ anyways, so it's not really an accurate statement, even if it is simply just an offhanded pejorative statement; I understand it's sarcasm and what it implies, I just disagree because of the veiled condescension.
That's provided by a Linter/LSP like Intellisense or clangd. (Finding the size of a structure of class.) Just use the (sizeof) keyword/function. -It's not hard to do the math yourself if you know the size of element the class is holding.- -Lets say you have a "Position2D" or "Point2D" class, it obviously needs to numbers to represent the coordinates, so 2 integers/floats (4 bytes * 2 elements).- -You might also want a constructor, in this example we'll treat them like function pointers, which on a 64-bit architecture is 8 bytes (a.k.a. a long pointer.)- -That's 16 bytes, not including the default class functions that modern C++ provides.- Finding the padding is near impossible to do programmatically as it's determined by the compiler. (This is why clangd and Sonar Lint suggest you state the padding of each element in a structure.) This is all from my understand(ing), I might be wrong so it's best to do your research.
Doesn't not deallocating at the end of a program cause memory leaks and also the possibility to loose the data, just like if you force terminated the program in task manager?
No, none of those actions leak any memory. It it the job of the OS to ensure that all memory from a process is reclaimed, if it didn't do that your memory would be gone in no-time (a program crashed and couldn't free its memory? Another 20MB never to be recovered! Nah...). So when a program terminates, either normally or forcefully (like with taskmgr), the OS will reclaim any memory that the process had not yet freed. Therefore, freeing when you are about to terminate your program is pointless, you're doing work that the os is going to do anyway, and when the os does it, it quickly cleans all your memory in one go instead of slowly one object at a time, which is what your own code has to do. All of this is also why it is okay to prototype code with leaks, it's not possible to eat away at the memory in between re-compiles, stopping the exe wipes away all leaks.
yes as the other comment suggested the os will clear everythingvery easily. Also most programs have leaks so it would have been a disaster if it didn't 😂😂
@@saniancreations I'm still in college (so I'm still a learner), we did plenty of tasks with memory allocation in C, and they told us to always clear the memory after we stopped using it. If you don't deallocate before the program ends and you run it with fsanitizer or Valgrind it will tell you that you have memory leaks
@@xx_sad_dam_whose_sane_xx That's because the expectation is that a memory leak is a bug in your program that had it been left running, would be unrecoverable memory without termination.
@@xx_sad_dam_whose_sane_xx Memory leaks are a problem so long as your application is running. When your program stops, all the leaks go away. If you have a program that runs for a long time or it performs a repeated operation, then not freeing memory is an issue because the memory usage accumulates over time, slowly growing bigger and bigger. _That_ is what you want to avoid. Most external diagnostic tools indiscriminately look at _all_ allocations, marking anything that is never freed as a leak. But not everything that isn't freed will cause your program to infinitely grow. For example, doing a one-time allocation at start-up and using it for the duration of the program will not grow your memory footprint over time. The only moment you need to free it is at the end, and in that case the OS can do it for you. I like to image leaks as actual water: A single droplet is okay. Not a leak. A glass of water is okay. Still not a leak. I can have a whole swimming pool in my house and that is still fine, because I deliberately put it there and it has a finite amount of water in it. But a _leak_ is a hole in your program, water pours out the hole indefinitely, until your house is flooded. That's a problem. But, "not freeing" is something you need to be careful with. If you _think_ you only allocate something once, but that code gets executed _again_ every so often through a weird edge-case, then you still have a leak on your hands, then your usage _will_ grow over time. So as a rule of thumb it is generally recommended to free all memory, just to be safe and to prevent stupid mistakes. In practice, for many small programs that have a clear beginning and end, not freeing memory is actually fine, it gets some memory to do a job, then it terminates and all the memory is cleaned up no-problemo. Pouring some water on the floor isn't a big deal if your house is about to get demolished. But if you have a large loop that can run an unknown number of iterations, you could end up pouring a thousand+ buckets of water on the floor, and you'll drown before the demolition crew gets there.
Let me tell you something about C++ & C based languages, there was a programming language & framework created for it that was forefront technology for web, it ended due to security & memory leak issues. What do I mean by this? The language built upon C++ was great but the base (C++ itself) was by design at fault causing the higher level programming language to often have security issues or poor memory management despite the language being managed by a very powerful company called Adobe. Lesson? Don't use languages that have failed to grow primarily due to faults in the programming language use proper programming languages that address this issues to avoid future problems.
@@lowlevelgamedev9330 I'll provide another study case, one regarding chrome (modern browser) & another with software from the past that had to be axed due to inherent issues with C++ or C. Chrome has had 4 Zero Day exploits in 2024, one involving issue with C++, A "type confusion" vulnerability occurs when a program allocates a piece of memory to hold a certain type of data but mistakenly interprets the data as a different type. This can lead to crashes, data corruption, as well as arbitrary code execution. Internet Explorer had similar issue again. This issue is from unsafe languages along with poor programming principles. In the long run one can't predict how secure their software might be. Internet Explorer in the past case had also similar issue with 4 exploits linked with issue in C/C++ this has resulted in businesses dumping the program altogether or using an alternative to build on top of (Edge Chromium). The main issue that needs to be addressed is poor programming principles, unsafe languages & whether programmers should opt for safer alternatives like RUST or GO, etc., As for NASA, this again falls on the programmer & how skilled they are. Again, what we can learn from this is that if there are better programmers chances of past mistakes can be lowered, but it doesn't hurt to experiment with other alternatives.
Very well put together video - I literally program nearly the same way, just with a bit more raii and also changing some of your rare usage of "new" keyword with malloc in my case for arrays - but as you point out: memory resources get so rarely hand-managed when done well that its a no-problem. This is also why I am not interested in rust - they solve a problem very complicatedly that with a good style one can easily solve themselves. Then rust fanboys compare newly written codes from them with hugely legacy decades old codebases with old style never looking like this to prove.... what exactly?
I do use vectors, just because cpp has a feature it doesn't mean I have to do it. There are modern languages like zig that have no private variables or no exceptions
@@lowlevelgamedev9330 Right and one day zig will replace C. I enjoy your videos for the record they are awesome and well done. But please understand something on this level of advise. There are two programming languages. There is C, and there is C++. There is no such thing as C/C++ programming language. These tricks for memory leaks and all that is all C language stuff and not C++. This causes confusion to new comers who want to learn C++. If you going to use C++ but code in C style then just code in C. If there is a feature of C++ you like then use it properly. This makes the codebase all over the place and introduces code rot. Zig for the record is awesome and I cannot wait for its 1.0 release!
@@romangeneral23 Sorry, but what makes this code "C style" rather than C++ style? Using automatic resource lifetime management with things like the stack, vector, unique_ptr, etc is very much a C++ thing and one C can't do and it's community has resisted adding. If you mean making classes and/or using inheritance, it is by no means C++ style to force those into a design without a reason. Same with exceptions. Same with new. Same with every other feature. I think you have the misconception. C++ is very much _not_ about forcing you into a particular style or design, but about giving you tools to meet the needs of your particular application. Nothing about the language forces you to use something you don't need.
@@insentia8424 Automatic resource management that is based on the stack absolutely is a thing in C++. Look up RAII and how destructors work if you aren't familiar with it.
It makes me sad to see you used C++ for 10 years, and still didn't know to implement assignment operators as copy/move-and-swap, or trying to use forward with const T& (it's relevant only for T&&). 😢
honestly I never implemented that operator except for that example 😂 so its not incredible the fact that I didn't do it properly. I don't use those features in my daily code
Well, I hate checking for iterator invalidation, plus vector is heap only, so bragging about not using smart pointers even with make_unique or make_shared is kind of crazy. Plus, I'm like 70% sure that vector is not actually guaranteed to be allocated contiguously. But yeah, temporary and arena allocators are great techniques, and in fact memory management systems are kind of crazy in big game engines.
well the point is not to have leaks so I don't care that vector is also heap, I just care that it doesn't leak like an unique pointer. Also yes the vector is 100% guaranteed to be contiguously.
@@lowlevelgamedev9330 by Backend I mean Core of the engine not the cream part of a Game. Ofcourse you might have a F 35 but who uses it to go to grocessory store. Just use a LMEV
I think it depends on the programming language, for example, C and C++ always had problems with memory allocation and pointers. But newer languages have other big issues. So you have to find the best fit and dive into it.
@@williamdrum9899 people are confused at pointers because they only learn what it does, not why it does that. so they dont understand why would they use them
7:21 this is not good advice, if it takes long for your program to do deallocations, simply hide your window to do it. It won't get in the user's way and you'll also be 100% sure that no resources were leaked
why dealocate in the first place, there is no such thing as leaking anything once you close your program. RAM VRAM loaded dlls handles, everything is cleared by the os when you exit, and it is also faster
Except for using smart pointers are semantically equivalent to using raw pointers correctly. Except for you. Don't have to actually generate the code to use them correctly yourself? [That is to say your code is probably buggy because you're not atomically reference counting your allocated pointers; there is a reason they do that in the standard library] And you're probably also actually leaking memory to the operating system [Windows really doesn't like you using opengl so they don't consistently free on exit vram allocated for opengl/Vulcan...] Which is the only explanation for your application taking longer to close when you actually properly free your memory which you should always do without question... Especially if you're only making five calls to new anyway.
My main point is that there is no valid reason to use the new keyword outside of a constructor, and even then you should tread carefully. And also no windows does not consistently free on exit. I've had, as recently as today, programs crash and stay resident and not boot because of that..
But to clarify exactly what I said, using the standard template library's unique_ptr type generates the exact same code as T:: operator new assuming you do not copy... And if you do copy, you are probably introducing bugs by not implementing a reference counter
Don't go Java. Everything is a pointer there. And have memory leaks. The stack is limited in size, all of your static data has to fit in it, and depending on the compiler it can be as little as 16kb to as much as 16mb, so there is also the risk of a stack overflow... and... you don't know how many times they slip in "new" for you in your new-less code, sorry to break the bubble... But that's the RAII paradigm for you.
@lowlevelgamedev9330 it is a myth Java doesn't leak. You simply have to mess the references to an object by not deleting appropriately containers or other kind of faulty memory management ruclips.net/video/Ml-jZipUzPk/видео.html
@@Spartan322 I mean, "leak" in the sense that it is lost memory you can't have (usually), but you can still "leak" memory in some sense by not removing all references of it from memory (like having some pesky static delegate somewhere or etc, even if it is a sign of smelly code).
@@diadetediotedio6918 That's not a leak, you might be erroneously retaining a memory location for your application, but memory leaks are quite well defined and without a memory leak in the JVM GC, it literally cannot leak, if that is a leak then any runtime static data is always a memory leak which is nonsense, an existing reference you could still access is not a leak, a memory leak must lose all references in the program and thus can only be cleaned up by OS upon termination of the program. (as without that memory reference the program can no longer recognize locate that memory to be manually deleted)
The limits of your language are really the limits of your world. And also, one youtuber is not a representative of "we" (assuming you mean programmers in general) and it certainly is not a representative of something we should be doing by itself, I see comments like this in almost any video where someone is using something even remotely close to what other people used in the past, even if the thing itself evolved in some way, it is surely "funny".
@@lowlevelgamedev9330 I found oop useful in cases where where you are modeling stuff properties and behavior, like video games or controlling devices. But I avoid class polymorphism like inheritance as most hierarchies are just arbitrary design decisions that are often bad decisions. Rust "objects" are ok. Mostly I do procedural even in Python. Functional programming is great for processing data, specially in high level languages. Rust functional features for example are ok. Pure functional languages are not worth the pain, as their obsession with avoiding side-effects and immutability is stupid in practice.
the last tip is horrible. not deallocating memory is just generally bad practice and should be avoided. the amount of time it takes to do that doesn't take that long and if you have so much memory allocated that it takes a while to close you just did something completely wrong
have you ever waited for a program to close? it happened to me. So tell me why should I dealocate the memory for textures that I use for my entire game? That's just waisted work. It is true that it probably won't take long but you don't have any reason to do it either
@@lowlevelgamedev9330 I mean if it takes that long to deallocate everything, you are almost guaranteed abusing malloc somewhat fierce with way too many small allocations. You have yourself shown strategies here to stop doing that. Sure you can just leave everything be on exit but that precludes the use of leak analysis tools and the like, which is not ideal.
@@lowlevelgamedev9330 Exactly. The only reason one would consider deallocating everything at the end would either be for correctness sake or because of the possibility of running that code on a system that doesn't free memory after a process is closed. To date, there is literally not a single active operating system that works like this, so... you are 100% correct. Not deallocating things that are going to be static is pretty much standard in the industry and is guaranteed to work basically everywhere. I mean, after all, what exactly is the difference between allocating a static buffer at compile time and allocating a heap buffer during runtime at the start of your program? the OS follows the exact same steps when loading the program and assigning the memory segments to the address space of the program. The only difference is that one is a static buffer with a size known at compile time, and the other is a static buffer with a size known during runtime (useful for loading files that can be modified, such as textures or any other assets). Besides... when you deallocate, your program is going to take longer between all the sleeping that the OS is going to put it through while its deallocating the reserved memory. Just closing the process allows the program to shut down visually for the user and then the OS can do the cleanup afterwards without having to make the end user wait any time for the program to shut down. This is just a common practice in the games industry, and one of the few practices that are actually good. I can understand people not liking it because it feels dirty, but it is still correctly exploiting the inner workings of the systems where the code will run, so...
in my C json parser I allocate memory only once, and it is while reading the files content, so then everything else that might need to access it's contents, like, we have a variable `"kind": "Person"`, instead of doing a copy of that memory, I just do a string view. so basically I just store the pointer to the start of the section I need from the string, and the length of it. I didn't did this in my first prototype, and for tokens I allocated 1024 bytes for each lexeme, but I figured out - mhh, maybe having a fixed limit on that is kinda stupid, and allocating 1024 extra bytes for each token is a bit of an over kill - so by doing a string view, I allocate 1 pointer + 1 size_t, so we just allocate 12 bytes in total :D ( this depends on the size of size_t in your computer and the size of a pointer, but it is still way better than allocating 1024 bytes ) so remember guys, never allocate memory if you don't need to, neither in the stack or the heap, just allocate as much memory as strictly needed for your program to work!
epic chad project 💪💪 thanks for sharing it with us
As a C newcomer, thx for that gigachad advice
You should probably still push the string_views to a tree-like structure for fast lookups
@@maksymiliank5135 the whole json document is stored in a tree, the string_views are just used for identifiers and stuff like that
Currently learning about this technique in Crafting Interpreters
1:30 the virgin "Player *p = new Player();" vs the chad "Player p;"
I am a virgin and I use "Player p;"
for real 💪💪💪💪
cant leak anything if everything is stored on the stack with a 1000 element array!
if you really need a lot of objects of a type and have to use the heap, i would just consider making a whole class to handle the memory alocation and deletion there and it would act just like the regular object, it's easier imo
edit: here's how the class would look like:
template
class HeapObject
{
protected:
T *ob;
public:
HeapObject()
{ob = new T;}
~HeapObject()
{delete ob;}
void operator=(T a)
{*ob = a;}
/// other methods, i can't be bothered to give more examples, it's just here to show that you can do it
T operator*()
{return *ob;}
};
@@atackhelikopter4303can even be an abstract class and the concrete ones will decide the implementations
that's the neat part: you don't
exactly
Since you are using containers to store your entities, your entities are on the heap (cuz the elements of a container are allocated on the heap), so you are not using stack memory at all. Besides this, nice advice!
yes I know, but that is not my concern, my concern is to not have memory problems. 💪
Contiguous heap memory, stack memory isn't a big deal when you need dynamic size tbf if you get cache locality out of it.
The only overhead from a heap is the allocation itself. If you allocate the objects as an array you still get all the benefits of cache locality and fast memory access of the stack. And if the size never changes then you don't need to reallocate so using this memory is essentially free.
@@lowlevelgamedev9330 but using std::vector literally comes with all of the caveats that using new and std::unique_ptr do, just wrapped up in a more convenient container. For example, you need to be ready for std::bad_alloc if you're considering that the other allocations can fail.
Generally, I really agree with your overall message. Stack allocation is always better than heap allocation, unless you actually need to heap allocate. Using std::vector and friends is an awesome way to manage dynamically allocated memory, too. However, consider using smart pointers instead of the five raw pointers you have (unless they aren't pointers to data that is owned by the object). There's essentially no weight at all for a std::unique_ptr and they are pretty easy to use, not to mention guaranteeing freeing allocated memory in the face of code refactoring and exceptions.
It is also good to add new tools to your toolkit!
4:21 you can probably get rid of the call to `new` in you filedata var. Make use of the appropriate overloaded constructor to create an std::vector initialized with X elements of unsigned char zero initialized aka std::vector fileData(fileSize). And the use it like this "file.read(fileData.data(), fileData.size());". This takes advantage of the fact that a vector is guaranteed to use contiguous memory to store its elements.
yeah I actually write my code like this now usually 💪, it just wasn't worth the effort changing that code there
To be honest, avoiding the heap for any programming at all is a great rule of thumb.
The edge cases where you'd actually need the heap are pretty few and far in between.
exactly 💪💪
I wouldn't call them "edge cases". When you want to load a large file like an image or audio, you need to store it on the heap because the stack isn't big enough. You also need the heap to store data structures like trees and graphs. Thankfully, the STL containers reduce the need for these kind of tasks but there are times when you need them.
@@TheLordoftheDarkness wouldn't mmap (or Windows equivalent) work better for that?
@@bigjango13 I can't tell because I still didn't learn how to use mmap. I've been willing to do so for a long time now though.
What I can say is that malloc is implemented using mmap on modern systems (it used to be implemented using brk and sbrk) so I guess they just do the same thing.
2:21 don't use raw arrays. Use std::array instead. They encode the size in them. Also the compiler would be able to make it as if you used a raw array. Aka zero overhead.
Technically C arrays have the size encoded too, problem is they love to decay into generic pointers which can absolutely stab you in the back.
It also has bounds checking in debug builds to help catch buffer overrun issues
@@Spartan322 there is a way to have a function take in a stack array, but it looks too bad and it's an additional unnecessary overload
@@MaxCE Yeah but because stack arrays love to decay into pointers it still has cases where it can perform unexpected results.
Just the thing I needed in my current development phase. Thanks for all the great tips, I'm really enjoying your videos.
std::pmr::vector my beloved
Is this the same like vector of pointers?
@@zanagi No, it's normal vector, but with different allocator (std::vector takes an allocator as second template argument). It basically allows you to preallocate memory block on stack and then suballocate from it. Allocations are fast and close in memory.
I love the pmr idea, but each time I use them I end up wondering how did I write something so ugly. I think c++26 will have a stack vector. Maybe I'll be able to use it at work by 2035.
@@fcolecumberri Yeah, I already moved to orthodox c++ so I don't use stl at all
Almost every component of my ECS is living on the stack 😅 .. who needs names or endless strings, user input is limited, e.g. player name is max. 24 characters.
Expect for textures, sound and other assets ... .. there are living in the resource loader, loaded once, freed at the end.
The only things that are RAII, are my Scenes/Levels, with init(), enter(), exit() ... Methods ... The Scene itself lives in a "Context" variable (held in the Scene manager). When switching scenes, the scene either gets "disabled" or uninit/init.
Everything else lives in the ECS registry/world, the scenes queries the components there need.
So the resource loader would be on the heap? < kind of new to c++
RAII is the stack though, like anything you create on the stack is managed through RAII, unless its a primitive data type, it will call the constructor on initialization and destructor on leaving the scope, that is RAII.
yo this sounds dope, I approve 💪💪
@@Spartan322 I assume these are defined in some top level function (maybe even in main) and passed around to other functions as pointers/references so RAII will not trigger as often
@@maksymiliank5135 Doesn't matter, RAII is stack allocation and that's still the stack, the object itself may allocate on the heap, like smart pointers or vector, but all memory behavior of the object itself is on the stack and anything that doesn't allocate on the heap is stack allocated and also performs RAII.
I wrote a memory management class in C++ which handles all memory allocations and deallocations with tracking and logging support. The same can be done with a struct if using C.
0:48 Step 1: Don’t allocate memory when you don’t need to
1:20 - Cool trick
1:37 - Cons and pros of allocating memory on the heap
1:54 - When to allocate on the stack or on the heap
3:46 Step 2: If you need to allocate memory, try to use containers
4:35 Step 3: Create specialized systems to manage memory for more difficult things
I once worked for a 2d pixel retro-rpg where i did the C++ part and since we are not in the 90ies any more and devices have more than 2 megabytes of RAM we could ignore a lot of the memory management by loading a lot of stuff at the start.
good stuff 💪💪
your channel has been invaluable to my coding hobbies, thank you!
My programs warn whenever they leak. I have a "generalized memory pools" technique. Dynamic memory allocation is nevermore a problem.
it's also pretty easy to have memory leaks in languages with garbage collection.
in java it's pretty easy to have memory leaks because you didn't dispose of an object in a specific way and then the garbage collector thinks it's still in use. and when you try to debug those issues it gets really ugly because it's hard to monitor what the garbage collector actually does. I always wish java had at least an option for doing manual memory management under certain circumstances
It seems so wierd to me that you can have memory leaks in gc languages 😂😂😂😂 can java get any worse?
@@lowlevelgamedev9330 ofc you can in any language with gc btw not just java. it just reduces the risk of memory leaks.
in special human error by the programer causes it in gc languages, in special when they don't know how the gc behaves in the language their working in.
It boggles my mind how many people keep insisting that you need to often deal with memory management in C++, like in almost every case I've observed, if you're managing memory directly, you should be using a container of some sort, or you should be using the stack. There are even only a limited set of reasons to deal with a raw pointer directly without managing it. Ownership semantics with smart pointers even support dynamic arrays now, so why would you never not use them? Probably because people don't bother the learn C++ and just keep doing things the C way.
@@bo4-x2n You don't know what a garbage collector is do you? C++ ownership semantics don't need a GC, you don't need a GC if you use RAII, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
@@bo4-x2n You need to learn what a GC is. C++ ownership semantics don't need a GC, RAII invalidates the need for a GC, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
@@bo4-x2n You need to learn what a GC is. C++ semantics don't need a GC, RAII invalidates the need for it, unique_ptrs are literally the same performance as manually performing the allocation, so unless you're claiming that C/C++ allocations are indistinguishable in performance to Java or C#, that's a self-clown comment. (technically the unique_ptr may be able to be more performant cause there is nothing actually preventing SBO, I don't recall of any implementation that does that, but they're technically allowed to)
2:40 let me introduce you to std::make_unique() and std::make_shared(). Almost never use the `new` operator to initialize a smart pointer.
well I don't actually use shared pointers like ever :))
@@lowlevelgamedev9330 shared pointers cause more problems than they solve in MOST cases so that's good. Unique pointers, however, are an improvement on using the new keyword in every single way so definitely use make_unique instead of using the new keyword if you end up needing to manage your own memory allocation, like when storing large objects on the heap.
@@lowlevelgamedev9330you dont have to … its still recommended to avoid new
And does that help you with memory fragmentation in any way? no? then what do you earn from this? the point of this video is for using stack allocated variables or preallocated static buffers for data. The more static allocations you do, the less you will need to dynamically allocate stuff, the less of a performance hit for your program (less fragmentation and less time wasted waiting for the OS to reserve a chunk of memory for you...). If you need to dynamically allocate, then do so in buffers or large chunks so that you don't have to allocate every single instance one by one individually. It's that simple. Using make_shared and make_unique does not save you from those problems. First, you should try to understand what the heap even is and how it works...
@@tiranito2834 You grossly misunderstood my comment.
I wrote a renderer with C++/Vulkan for 2D cad stuff and even in 2-3k lines of code I use new all of 3 times to allocate some command pool objects. Don't need new that often.
using containers is just like using new/malloc, cuz they all allocate memories on the heap (besides themselves on the stack)
yes and no, containers use the RAII way of habdeling memory.
The constructor (or setters) allocate the memory, and the destructor ensures the memory is deleted, even if an exception is thrown. the std can throw exceptions (unless you explicitely ask it not to), same goes for most c++ libraries.
people think there is a limit to the stack size there isn't the at least on windows the exe NT headers contain a 64 bit slot for the stack size needed by the program
Allocating on the heap is fine. What matters is whether you can predict how much memory you'll need. If you can't, then you won't be able to manage your memory because you don't have a good concept of what it's doing. If you can, then it's simple to just free it all at once way later when you're done with it. As long as you're not interleaving tons of mallocs and frees you won't get into trouble, and that's to say nothing of what's possible when you realize malloc is garbage and you can easily do syscalls yourself. This is the true meaning "of don't allocate more than you need to", advice that's been misconstrued greatly over the years.
Could you make a more detailed video about memory arenas?
I wish the entirety of the r/Cplusplus subreddit would watch this video and stop for one post crying about pointers and memory unsafety. Computer memory is NOT that complex. You allocate bytes. Write shit into them. And then deallocate them. You can do 3 things with memory. If you make it more complicated than that, that's on you.
Totally agree, also it's funny that I never use smart pointers and I don't complain about memory problems :)))
Never heard of valgrind? I use that quite a bit, helps quite a bit.
I am on windows, tho fortunatelly I dom't really need it. Also I don't think it can help with gpu memory :(( I usually have to deal with that more often
@@lowlevelgamedev9330 Ooof Windows, my condolences. Switching over to linux made my life as Dev so much better. Anways I recommend you add some debug logic that keeps track of allocated vram, maybe add it to a debug overlay or something along the lines.
Ayoo step 3 is how I do it in JavaScript even though it is not necessary. It just feels right.
How do you handle id invalidation? If you were to create a large complex game, you can't have all rresources loaded from the beggining, and the int index variable might point to an incorrect resource when the resource manager has to delete elements
good questoon, I wod store them in an unordered map, and if an entity is deleted, it just will dissapear from the map, so I can just check if the entry is in the map to know if it exists
What would you then suggest when you have a situation where class A has a member B, but which it doesn't want to initalize straight on A construction, as perhaps it has to do something else first. One could then just let B have an empty constructor and use an Init method but this then goes against RAII, so it seems to me like the only two options in this case is break RAII or use a unique_ptr
break the raii principle lol, I use an init method all the time, and I have a deinit method. Besides std vector and other containers I don't have any raii in my code. For many cases its very convinient to write code like that.
@@lowlevelgamedev9330Why not use std::optional?
Basically, if you need to deal with memory allocation:
1. Thou shalt call one 'delete' for every 'new'.
2. Separate memory management tasks from other tasks.
3. The "big 3" for a class with memory allocation to avoid crashing: copy constructor and assignment operator for deep copies, and destructor for cleanup.
I mean 1 is an oversimplification, if you have a simple system you do that or raii, but for a complicated system it won't look as clean
1. Just use smart pointers
Love your content. Are you planning to make a video on your coding style/structure ?
yo that is a good idea, yes 💪
@@lowlevelgamedev9330 nice!! I noticed your conventions seem clear, no bullshit, efficient !
I always get a crisis when I can't use a stack.
😂😂
If you don't handle threads at the end of program then it will crash and take even longer to close.
that is another story, and they crash because that is a mechanism made by the cpp standard library, to help you not forget to either detatch them or join them. If you make them using windows api I don't think it will crash
there's also alloca if you wanna allocate something of unknown size on the stack, yet another L for the heap
yeah but you shouldn't really use that as far as I know, it can have very wierd problems
@@lowlevelgamedev9330 it's only really problematic if you still treat it as a heap allocation since it's always going to get popped at the end of the scope, so just don't do dumb stuff like returning the pointer or passing it around to other threads and it's pretty much fine to use, it's perfect for small stuff like the opengl shader compilation message
Totally agre on 1:30. Some seems to use alloc & pointers way too much even when not needed - just KISS! (Keep It Simple, Stupid)
also Destroyers are usefull to make sure delete stuff as soon the Class is destroyed..
well I don't use destroyers in my coding style, except you know vectors and stuff have them, maybe I will make a video on it
@@lowlevelgamedev9330 oh ok.. that's fair.. yeah, vectors does the job automatically so.. its ok..
Anyone have a link to an explainer on why heap allocated memory may be accessed slower?
I thought access times after allocation/the first access would be just as fast as stack allocated memory.
The stack is on the hot code path so more likely to be in the cache, also the heap can be fragmented resulting in non contiguious memory which inhibits prefetching to the cache, and multiple layers of indirection like vector of vectors will makes this much much worse.
If you are interesting data oriented design is what you need to look at.
I had a stroke reading that title
its gramatically correct but its understandable coming from a f*rry
6:43 The index also gets invalidated, so i dont really see the benefit of this tip.
Sure on most cases you'll save 4 bytes on 64bit cause the pointer is 8bytes and your int textureIndex would be enough for any game and is only 4.
Apart from that, you're passing in the texturemanager and then do a read with the index.
That still boils down to reading the underlying pointer since your std::map/unordered_map is heap allocated.
So it my mind that is the same speed, maybe even slower. Correct me if im wrong though.
so for the first thing, for example there are 5 players each one with a skin, and they can change. The index won't be invalidated, there are always 5 players, the skin can. That was the case that I was thinking about. If the index can get invalidated you would make sure to check for that of chourse. Also for the second thing I was thinking about sending the gpu id directly, like sending the opengl id, but it doesn't matter, the speed is the same roughly but I mentioned that it can be even faster to make my point very clear.In practice it boils down to how the compiler optimizes it and it is the case that for this thing it can be slightly faster or slower but very small difference
Question: How to avoid memory leaks when allocation new objects?
Answer: Don't
👍
Also you can allocate stuff upfront and use them as needed
2:50 es mejor usar std::make_unique si no vas a inicializar el std::unique_ptr con un destructor personalizado es mejor porque:
- Sigue la regla de evitar el uso de "new" porque actualmente hay mejores opciones para manejar la memoria
- "auto p = std::make_unique()" es más corto y conciso que "std::unique_ptr p(new Player{})
- std::unique_ptr está implementado para ser seguro ante las excepciones
So basically code C++ like how you code in C, only use C++ features if necessary, and avoid unnecessary use of heap
yes you got it 💪💪 advanced cpp is usefull for things like std::vector but its just too complicated to write that every day
you got a pdf or something?
Was thinking about this, but my school's game engine uses shared_ptr GameObject and components as a base. Im not sure how i can code without any ptr.
well you know all the schools do that but you don't have to do the same in your code 💪💪 their example is just a simple didactic thing, I don't even use ecs in my games lol
what if you have a function that returns int how do you check for error code?
well I would return an error code as an enum, or, if I need a return, I could take an extra parameter, as a refference, to write the error into that.
I am also making a clone of minecraft and have not yet used the keyword “new” (and will not use it)
most chad dev in the room, you can send me a link in my discord and tag me I'm curious
So this is why people say C++ isn't memory safe.
I never understood this. I almost never use new. Like ever. It feels like reinventing the wheel, so I only do it when absolutely needed.
"So this is why people say C++ isn't memory safe."
People say that cause the language it self is not memory-safe and sadly many of the old folks start by first teaching their half-knowledge of C and then their abysmal pre C++98 style.
@@ABaumstumpf By that reasoning no language is safe.
Some are just more restrictive than others.
@@LokiScarletWasHere There are many languages where simple memory access it checked - there is nothing in C++ preventing you from creating an array of 2 elements and then writing to the 1235235 index. You can create a pointer to any arbitrary memorylocation and write to it. That is what i means with the language is not memory safe.
@@ABaumstumpf Again, no language is safe. Some are just more restrictive than others.
You just have to get a bit more creative with more restrictive languages if you want to make something that'll compile but segfault.
Safety does not exist outside the programmer and there is no free lunch.
Even the choice of language, how many training wheels the programmer rides with, is a decision by the programmer, or a decision made for the programmer if the code already existed before they entered the project.
@@LokiScarletWasHere Ah, so you are just intentionally pedantic and dishonest - then have fun.
When i did texture manager for my little doom like engine because of memory leaks everyone called me stupid. But look at me now i was stupid but at least solution was smart.
lol that's how top gamedevs do it in game dev. If you look at top gamedev programmers that's what they talk about and they don't use unity they make unity :))
Okay, you touched on a subject I've been wanting to know about for long lol
I have a question tho, I remember once having a vector of entities, the vector was getting passed around quite a lot, you see I have this preference of passing things as parameters instead of making them accessible globally or make a system for them (Unless the need arises)
However, I had a big problem where the entities vector was getting corrupted somehow? I don't really understand the problem myself but I remember it had an addressing issue, it got fixed when I allocated the entities on the heap
So my question: if this is wrong, how wrong is it? and how can I potentially fix it?
You either used the vector wrong or one of the functions you passed it in mutate it accidentally. Either pass it as a const reference (no mutation possible) or as a reference (mutation possible).
@@sledgex9 I am sure I was not mutating, the vectors got passed as a "const &" since it was large
The issue is that i noticed is that the entities within had different addresses for some reason, which was very strange to me
@@nazihboudaakkar8117 Unfortunately this kind of problem can't be diagnosed via a comment. All I can say is that vectors should work with no problems. Potentially a misuse was happening somewhere. C++ has many pitfalls you can fall into if you aren't experienced, unfortunately.
If you take a pointer to an item in a vector and then you add/remove things from the vector, it will invalidate the pointer because under the hood reallocation happened.
@@swansonair As I said earlier, I was passing the vector around as a const reference precisely to avoid that and to avoid needlessly copying the contents
The issue got solved the moment i changed the entities to be on the heap, I clearly remember not touching anything (besides refactoring things to use the unique_ptr) and It felt sooo awkward
But yeah, it was probably a bug on my part
about allocating on the heap, inst it useful for class dependency management?
like, hear me out... usually i have a codebase that only uses makefiles and very frequent OOP (don't ask why, i just don't have patience to learn CMake), and by doing so, i frequently encounter problems with changing a certain class to add a new member causing me to recompile the entire project only to make the dependant classes work accordingly, because if i don't, i can start modifying data that i shouldn't be or just cause some really "undebuggable" issues. so my usual solution is to abstract each member with a getter and a setter function, so i don't have to worry about using the . or the -> operator in raw members and alignment, since it will be dealt with when compiling the single c++ file.
however i do understand where you come from... nice tips!
that doesn't really sound good :))) and I also don't get how what you are describing has to do with the heap
@@lowlevelgamedev9330 if i use the stack, the size for structs and classes will be fixed, like:
struct A {};
struct B { A a; int bar; };
So, ignoring the class optimization thing that some compilers do, and considering that A and B are on different headers and .cpp files respectively, if I change A to this:
struct A { int foo; };
For the .cpp file of B, it still thinks that A has not changed, but A has changed, so A.foo becames the same as B.bar, making some really weird bugs and eventually leading to UB.
My fix is to defined the heap allocator for each class inside the .cpp files and wrap foo and bar with getters and setters
Never understood why heap allocation was used so much, still don't and my projects run fine
I should really learn though
well people just allocate for no reason that's why
all these memory leaks are from people using c with classes not c++
There are no classes in C, lol. Wtf are you going on about?
@@daemoncluster it’s what that style of programming is called. It’ll claim to be c++ but the programmer will lean more towards the C side of things and only use a small percentage of features from c++ (i.e. the classes)
@@flyingsl0ths Yeah, because C++ is a overly complicated and redundant mess. Even the creators and maintainers can't keep it straight. People think all C is missing is support for classes and automated memory management. C++ went above, beyond, and into completely esoteric territory with features like meta-programming, generics, etc. C is not easy, but it doesn't have the mental overhead that C++ tempts you with. Regardless, C with classes just ends up being C++ anyways, so it's not really an accurate statement, even if it is simply just an offhanded pejorative statement; I understand it's sarcasm and what it implies, I just disagree because of the veiled condescension.
The stack 🗿 we really underestimate it
yes fr, you can't really fill it unless you do recursive stuff or really have some big classes
just deallocate whatever you allocate, duhh 🙄
1:58 how do you see size and alignment of class? :)
idk just visual studio tells me, maybe it is the productivity powertools extension that I have but I think it is just a visual studio feature
@@lowlevelgamedev9330 ok thanks 👍
That's provided by a Linter/LSP like Intellisense or clangd.
(Finding the size of a structure of class.)
Just use the (sizeof) keyword/function.
-It's not hard to do the math yourself if you know the size of element the class is holding.-
-Lets say you have a "Position2D" or "Point2D" class, it obviously needs to numbers to represent the coordinates, so 2 integers/floats (4 bytes * 2 elements).-
-You might also want a constructor, in this example we'll treat them like function pointers, which on a 64-bit architecture is 8 bytes (a.k.a. a long pointer.)-
-That's 16 bytes, not including the default class functions that modern C++ provides.-
Finding the padding is near impossible to do programmatically as it's determined by the compiler.
(This is why clangd and Sonar Lint suggest you state the padding of each element in a structure.)
This is all from my understand(ing), I might be wrong so it's best to do your research.
@@somedudenamedanthony why tf you wrote all that. I goggled it, I update visual studio. Will test it later
Doesn't not deallocating at the end of a program cause memory leaks and also the possibility to loose the data, just like if you force terminated the program in task manager?
No, none of those actions leak any memory. It it the job of the OS to ensure that all memory from a process is reclaimed, if it didn't do that your memory would be gone in no-time (a program crashed and couldn't free its memory? Another 20MB never to be recovered! Nah...). So when a program terminates, either normally or forcefully (like with taskmgr), the OS will reclaim any memory that the process had not yet freed. Therefore, freeing when you are about to terminate your program is pointless, you're doing work that the os is going to do anyway, and when the os does it, it quickly cleans all your memory in one go instead of slowly one object at a time, which is what your own code has to do. All of this is also why it is okay to prototype code with leaks, it's not possible to eat away at the memory in between re-compiles, stopping the exe wipes away all leaks.
yes as the other comment suggested the os will clear everythingvery easily. Also most programs have leaks so it would have been a disaster if it didn't 😂😂
@@saniancreations I'm still in college (so I'm still a learner), we did plenty of tasks with memory allocation in C, and they told us to always clear the memory after we stopped using it. If you don't deallocate before the program ends and you run it with fsanitizer or Valgrind it will tell you that you have memory leaks
@@xx_sad_dam_whose_sane_xx That's because the expectation is that a memory leak is a bug in your program that had it been left running, would be unrecoverable memory without termination.
@@xx_sad_dam_whose_sane_xx Memory leaks are a problem so long as your application is running. When your program stops, all the leaks go away. If you have a program that runs for a long time or it performs a repeated operation, then not freeing memory is an issue because the memory usage accumulates over time, slowly growing bigger and bigger. _That_ is what you want to avoid. Most external diagnostic tools indiscriminately look at _all_ allocations, marking anything that is never freed as a leak. But not everything that isn't freed will cause your program to infinitely grow. For example, doing a one-time allocation at start-up and using it for the duration of the program will not grow your memory footprint over time. The only moment you need to free it is at the end, and in that case the OS can do it for you.
I like to image leaks as actual water: A single droplet is okay. Not a leak. A glass of water is okay. Still not a leak. I can have a whole swimming pool in my house and that is still fine, because I deliberately put it there and it has a finite amount of water in it. But a _leak_ is a hole in your program, water pours out the hole indefinitely, until your house is flooded. That's a problem.
But, "not freeing" is something you need to be careful with. If you _think_ you only allocate something once, but that code gets executed _again_ every so often through a weird edge-case, then you still have a leak on your hands, then your usage _will_ grow over time. So as a rule of thumb it is generally recommended to free all memory, just to be safe and to prevent stupid mistakes.
In practice, for many small programs that have a clear beginning and end, not freeing memory is actually fine, it gets some memory to do a job, then it terminates and all the memory is cleaned up no-problemo. Pouring some water on the floor isn't a big deal if your house is about to get demolished. But if you have a large loop that can run an unknown number of iterations, you could end up pouring a thousand+ buckets of water on the floor, and you'll drown before the demolition crew gets there.
Let me tell you something about C++ & C based languages, there was a programming language & framework created for it that was forefront technology for web, it ended due to security & memory leak issues. What do I mean by this? The language built upon C++ was great but the base (C++ itself) was by design at fault causing the higher level programming language to often have security issues or poor memory management despite the language being managed by a very powerful company called Adobe.
Lesson? Don't use languages that have failed to grow primarily due to faults in the programming language use proper programming languages that address this issues to avoid future problems.
aircrafts and nasa still use c and cpp. I think it is kinda important for them to not have leaks
@@lowlevelgamedev9330 I'll provide another study case, one regarding chrome (modern browser) & another with software from the past that had to be axed due to inherent issues with C++ or C. Chrome has had 4 Zero Day exploits in 2024, one involving issue with C++, A "type confusion" vulnerability occurs when a program allocates a piece of memory to hold a certain type of data but mistakenly interprets the data as a different type. This can lead to crashes, data corruption, as well as arbitrary code execution. Internet Explorer had similar issue again. This issue is from unsafe languages along with poor programming principles. In the long run one can't predict how secure their software might be. Internet Explorer in the past case had also similar issue with 4 exploits linked with issue in C/C++ this has resulted in businesses dumping the program altogether or using an alternative to build on top of (Edge Chromium). The main issue that needs to be addressed is poor programming principles, unsafe languages & whether programmers should opt for safer alternatives like RUST or GO, etc.,
As for NASA, this again falls on the programmer & how skilled they are.
Again, what we can learn from this is that if there are better programmers chances of past mistakes can be lowered, but it doesn't hurt to experiment with other alternatives.
I'm gonna be that person... Just use rust 😄
music name?
yo check the description 💪
I never use "new". Not even once.
I use malloc. 🤣
Very well put together video - I literally program nearly the same way, just with a bit more raii and also changing some of your rare usage of "new" keyword with malloc in my case for arrays - but as you point out: memory resources get so rarely hand-managed when done well that its a no-problem.
This is also why I am not interested in rust - they solve a problem very complicatedly that with a good style one can easily solve themselves. Then rust fanboys compare newly written codes from them with hugely legacy decades old codebases with old style never looking like this to prove.... what exactly?
Fully agree, for me its the same
I hate people who overuse input arguments with a passion of a thousand suns.
So you are coding in C but saving the file as a .cpp ? Then code in C
I do use vectors, just because cpp has a feature it doesn't mean I have to do it. There are modern languages like zig that have no private variables or no exceptions
@@lowlevelgamedev9330 Right and one day zig will replace C. I enjoy your videos for the record they are awesome and well done. But please understand something on this level of advise. There are two programming languages. There is C, and there is C++. There is no such thing as C/C++ programming language. These tricks for memory leaks and all that is all C language stuff and not C++. This causes confusion to new comers who want to learn C++. If you going to use C++ but code in C style then just code in C. If there is a feature of C++ you like then use it properly. This makes the codebase all over the place and introduces code rot.
Zig for the record is awesome and I cannot wait for its 1.0 release!
@@romangeneral23 Sorry, but what makes this code "C style" rather than C++ style? Using automatic resource lifetime management with things like the stack, vector, unique_ptr, etc is very much a C++ thing and one C can't do and it's community has resisted adding.
If you mean making classes and/or using inheritance, it is by no means C++ style to force those into a design without a reason. Same with exceptions. Same with new. Same with every other feature.
I think you have the misconception. C++ is very much _not_ about forcing you into a particular style or design, but about giving you tools to meet the needs of your particular application. Nothing about the language forces you to use something you don't need.
@@oracleoftroy The stack is not a C++ thing, nor is a it a thing C has no access to. It's specific to the OS and architecture you are programming for.
@@insentia8424 Automatic resource management that is based on the stack absolutely is a thing in C++. Look up RAII and how destructors work if you aren't familiar with it.
which font did you use in the video?
-fsanitize=address
It makes me sad to see you used C++ for 10 years, and still didn't know to implement assignment operators as copy/move-and-swap, or trying to use forward with const T& (it's relevant only for T&&). 😢
honestly I never implemented that operator except for that example 😂 so its not incredible the fact that I didn't do it properly. I don't use those features in my daily code
Man this is amazing, you could probably make a prototype game like mount and blade warband with your engine.
Well, I hate checking for iterator invalidation, plus vector is heap only, so bragging about not using smart pointers even with make_unique or make_shared is kind of crazy. Plus, I'm like 70% sure that vector is not actually guaranteed to be allocated contiguously. But yeah, temporary and arena allocators are great techniques, and in fact memory management systems are kind of crazy in big game engines.
well the point is not to have leaks so I don't care that vector is also heap, I just care that it doesn't leak like an unique pointer. Also yes the vector is 100% guaranteed to be contiguously.
malloca 🧐
This vdo has enlighten me that I don't need Cpp for my game programming.
Its just Good for Backend
Who uses Cpp for Platformer
what :) who uses cpp for backend. Cpp is used for gamedev very often
@@lowlevelgamedev9330 by Backend I mean Core of the engine not the cream part of a Game.
Ofcourse you might have a F 35 but who uses it to go to grocessory store.
Just use a LMEV
It used to be so easy. Nowadays computers are so complex nobody knows how they really work so we end up confusing ourselves further
I think it depends on the programming language, for example, C and C++ always had problems with memory allocation and pointers. But newer languages have other big issues. So you have to find the best fit and dive into it.
@@desenhonatorre I firmly believe that C is the reason pointers are confusing. In assembly they're not that hard to grasp
@@williamdrum9899 people are confused at pointers because they only learn what it does, not why it does that. so they dont understand why would they use them
- Look at me I dont use new anywhere really
- Oh, btw I just use containers and obfuscate the new calls and allocations, dw shh
well yes but that means I don't have leaking problems, what were you expecting, getting rid of leaks magically?
4:54 Isn't that called... handles?
Simple trick: you don't deal with memory leaks, you just but more RAM
hello
hi
how are you
My wife left me
7:21 this is not good advice, if it takes long for your program to do deallocations, simply hide your window to do it. It won't get in the user's way and you'll also be 100% sure that no resources were leaked
why dealocate in the first place, there is no such thing as leaking anything once you close your program. RAM VRAM loaded dlls handles, everything is cleared by the os when you exit, and it is also faster
you use Rust
BA DUM TSSSSS
Use rust instead of cpp
Nu uh
You can just use smart pointers and you're done
well as mentioned in the video, they won't help you with difficult cases. You can't manage gpu memory like that for example
Except for using smart pointers are semantically equivalent to using raw pointers correctly. Except for you. Don't have to actually generate the code to use them correctly yourself? [That is to say your code is probably buggy because you're not atomically reference counting your allocated pointers; there is a reason they do that in the standard library] And you're probably also actually leaking memory to the operating system [Windows really doesn't like you using opengl so they don't consistently free on exit vram allocated for opengl/Vulcan...] Which is the only explanation for your application taking longer to close when you actually properly free your memory which you should always do without question... Especially if you're only making five calls to new anyway.
um I didn't really understood your comment but windows without question will free all the resources and it will do it quickly :))
My main point is that there is no valid reason to use the new keyword outside of a constructor, and even then you should tread carefully. And also no windows does not consistently free on exit. I've had, as recently as today, programs crash and stay resident and not boot because of that..
But to clarify exactly what I said, using the standard template library's unique_ptr type generates the exact same code as T:: operator new assuming you do not copy... And if you do copy, you are probably introducing bugs by not implementing a reference counter
Don't go Java. Everything is a pointer there. And have memory leaks.
The stack is limited in size, all of your static data has to fit in it, and depending on the compiler it can be as little as 16kb to as much as 16mb, so there is also the risk of a stack overflow... and... you don't know how many times they slip in "new" for you in your new-less code, sorry to break the bubble... But that's the RAII paradigm for you.
can you have leaks in java? I didn't know that, if you have some details I would love to hear them so you can tag me on my discord 💪💪
@@lowlevelgamedev9330 Not unless the JVM GC has a memory leak. The GC ensures there are no memory leaks.
@lowlevelgamedev9330 it is a myth Java doesn't leak. You simply have to mess the references to an object by not deleting appropriately containers or other kind of faulty memory management ruclips.net/video/Ml-jZipUzPk/видео.html
@@Spartan322
I mean, "leak" in the sense that it is lost memory you can't have (usually), but you can still "leak" memory in some sense by not removing all references of it from memory (like having some pesky static delegate somewhere or etc, even if it is a sign of smelly code).
@@diadetediotedio6918 That's not a leak, you might be erroneously retaining a memory location for your application, but memory leaks are quite well defined and without a memory leak in the JVM GC, it literally cannot leak, if that is a leak then any runtime static data is always a memory leak which is nonsense, an existing reference you could still access is not a leak, a memory leak must lose all references in the program and thus can only be cleaned up by OS upon termination of the program. (as without that memory reference the program can no longer recognize locate that memory to be manually deleted)
It's funny how nowadays we are slowly going back to procedural programming and leaving OOP
yeah function pointers within structs is all you need actually
well it's normal. Procedural is both how people think and also how the computer thinks so idk who is thinking that functional programming is good :))
The limits of your language are really the limits of your world.
And also, one youtuber is not a representative of "we" (assuming you mean programmers in general) and it certainly is not a representative of something we should be doing by itself, I see comments like this in almost any video where someone is using something even remotely close to what other people used in the past, even if the thing itself evolved in some way, it is surely "funny".
@@lowlevelgamedev9330 I found oop useful in cases where where you are modeling stuff properties and behavior, like video games or controlling devices. But I avoid class polymorphism like inheritance as most hierarchies are just arbitrary design decisions that are often bad decisions. Rust "objects" are ok. Mostly I do procedural even in Python. Functional programming is great for processing data, specially in high level languages. Rust functional features for example are ok. Pure functional languages are not worth the pain, as their obsession with avoiding side-effects and immutability is stupid in practice.
the last tip is horrible. not deallocating memory is just generally bad practice and should be avoided. the amount of time it takes to do that doesn't take that long and if you have so much memory allocated that it takes a while to close you just did something completely wrong
have you ever waited for a program to close? it happened to me. So tell me why should I dealocate the memory for textures that I use for my entire game? That's just waisted work. It is true that it probably won't take long but you don't have any reason to do it either
@@lowlevelgamedev9330 because you're closing your program, you don't need the texture anymore
@@lowlevelgamedev9330 I mean if it takes that long to deallocate everything, you are almost guaranteed abusing malloc somewhat fierce with way too many small allocations. You have yourself shown strategies here to stop doing that. Sure you can just leave everything be on exit but that precludes the use of leak analysis tools and the like, which is not ideal.
@@lowlevelgamedev9330 Exactly. The only reason one would consider deallocating everything at the end would either be for correctness sake or because of the possibility of running that code on a system that doesn't free memory after a process is closed. To date, there is literally not a single active operating system that works like this, so... you are 100% correct. Not deallocating things that are going to be static is pretty much standard in the industry and is guaranteed to work basically everywhere. I mean, after all, what exactly is the difference between allocating a static buffer at compile time and allocating a heap buffer during runtime at the start of your program? the OS follows the exact same steps when loading the program and assigning the memory segments to the address space of the program. The only difference is that one is a static buffer with a size known at compile time, and the other is a static buffer with a size known during runtime (useful for loading files that can be modified, such as textures or any other assets).
Besides... when you deallocate, your program is going to take longer between all the sleeping that the OS is going to put it through while its deallocating the reserved memory. Just closing the process allows the program to shut down visually for the user and then the OS can do the cleanup afterwards without having to make the end user wait any time for the program to shut down. This is just a common practice in the games industry, and one of the few practices that are actually good. I can understand people not liking it because it feels dirty, but it is still correctly exploiting the inner workings of the systems where the code will run, so...
Not deallocating memory is fine. Not closing a file or socket is not fine.
🤍
cmak
ekamc