Stack vs Heap Memory in C++

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up guys my name is achernar and welcome back to my say boss boss series so today we're gonna be talking all about memory and in fact we're gonna be talking about two different types of memory in C++ the stack and the heap which you may have heard a lot about and might not be a hundred central and what that means why they're used when they're used and how all that works and why you even care about how that works okay so first of all the second of heat what are they well when our program starts it gets divided into a bunch of different like areas of memory and there's a lot more than just a stack in the heap but the two kind of ones that we care about most I would say would be the stack and the heap now in terms of how application is launched and what the operating system does is loosely it will load the entire program into memory as well as allocate a whole bunch of physical RAM so that our actual application can run now the stack and the heap are two areas that we actually have in our RAM the stack is typically an area of memory that has a predefined size usually around two megabytes or so and the heap is an area that is also kind of predefined to a default value however it can grow and change as our application goes on now it's important to note that the actual location the physical location of these two areas of memory is ultimately the same it's in our Ram a lot of people tend to think that the stack might be something that's stored in the CPU cache or something like that whilst it is likely to be hot in the case because we're continually accessing it that's we can't like not all of it will be stored in our cache and that's not how any of that works so just keep in mind that the actual location of those two areas of memory is in our memory right that's why they're two different areas of our memory now memory in our program is used so that we can actually store data we need a place to store the data that we need to run our program whether that be local variables or maybe we've read stuff in from a file and we need to process that data all that kind of stuff we need a place to store that and the stack in the heap they are areas in which we're allowed to store data now they work very very differently but fundamentally what they do is the same we can ask C++ to give us some memory from either the stack or the heap and it will give us a block of memory of our requested size if everything as well the difference is how it allocates that memory for us so suppose for an example that we want to be able to store an integer an integer on most platforms is 4 bytes an int right so how do we find a contiguous block of 4 bytes of memory contiguous just means in a row right how do we find that block of 4 bytes of memory the way that the stack will give us that memory versus the way that the heap will give us that memory that's different and when we asked for memory like that that's called the memory allocation or just an allocation for sure so let's take a look at some of the differences between allocating and ant and maybe various other kind of pieces of data on the stack versus the heap in our actual C++ code so I'm going to show you how to allocate three different types of data over here in our program first of all we'll start off with an int so I'll just write int value equals 5 that's it that's how you allocate an integer on the stack and in fact we're also giving it the value 5 if we were to do this on the heap it would look like this in pointer H I'll say H value 4 heat value equals new int and then we have to dereference that H value and give it the value 5 ok so you can notice that it is two lines of code but more importantly we actually use the new keyword here to allocate on the heap that's what kind of distinguishes these two allocations that is a stack allocation and this is a heap allocation with arrays it's kind of similar we have int array and then 5 for example for five elements in the array and on the heap this would look like this in H array for heap array equals new int five like that so again the major difference being where we're using the new keyword here to actually allocate that memory and then finally I'll show you a quick example with an object so if I make a struct here or a class doesn't really matter called vector three and this will just have three floats X Y Z if I was to allocate this on the stack it would look like this vector three vector and on the heap it would look like this vector three pointer H vector equals new vector 3 and these parentheses are of course optional but I like to keep them in now before I run this application and step through and show you how it actually works let's actually give these these variables of ours default values so this inner ray has absolutely nothing right now so let's maybe give it some values I'll say array 0 equals 1 and then let's also give these just ascending numbers 2 3 4 5 that should be easy enough to see I'm going to copy and paste this and same for our age array making sure that I of course type in age here and then for our vector I'm just going to use the default constructor here to actually give it some values so we'll say X will be 10 by default and Y will be 11 and Z will be 12 now these are floats so they might look a little bit different in memory but it should be okay all right cool so now so now let's put a breakpoint over here and hit f5 so the first thing I'm going to do is actually go to the memory address of values to fight type an ampersand value in my memory view over here you can access the memory view by going to debug windows memory and then memory one you can see that it takes me to the memory address of that variable now ccccc in debug mode means that we just haven't actually initialized that value yet so if I hit f10 you can see that we change to five so now we have that value in our actual memory pretty simple now let's hit f10 and see where this array actually is so I'm just gonna type in a right order to use a pointer cuz it's an array we'll see where we are okay pretty cool so we have one here let's hit f10 and then again and then again and then again okay cool so check that out we have one two three four five in our memory all in a row brilliant now if we look over a few bytes we also have five over here right and that five happens to be our value five so if I go to the memory address of value once more you can see that if I scroll up just a little bit that's my value and that is my actual array so they're right next to each other now there are some bytes in between the two that's just because we're running in debug mode it's actually just adding safety guards kind of around all about variables to make sure that we don't overflow them or access them in the wrong memory address and all that kind of stuff and finally we have our vector so if I hit f10 and then I look at the actual memory address of our vector you can see there over here we have 0 to 0 for 100 ah basically we have our 12 bytes here that represents our vector a bunch of safety guards and then the 1 2 3 4 5 array and then that 5 variable so it's all really kind of close together in memory because what actually happens is when we allocate variables in the stack all it happens is the stack pointer that is the pointer of like the top of the stack basically just moves moves that amount of bytes so if I want to allocate an integer that's 4 bytes we move the stack pointer 4 bytes said if I want to allocate an array like we have here five integers that's four times five or twenty 20 bytes the stack pointer moves 20 bytes and finally for our vector three we have three floats each float is four bytes so 12 bytes we just move the stack pointer that is it the the memory is literally stored on top of each other like a stack now in most stack implementations we actually grow the stack backwards which is why you're seeing higher memory addresses having like the first value so int value is actually stored at a higher memory address and then we kind of store the the array next to it but kind of backwards at a lower memory address value and then because it kind of grows backwards but the idea of a stack is we literally just stack things on top of each other which is why a stack allocation is extremely fast it's literally like one CPU instruction all we do is we move the stack pointer and then we return the address of that stack pointer that's it so again I want to allocate an integer that's four bytes I move the stack pointer in this case backwards four bytes and I return that memory address because that is the beginning of my block of 4 bytes that is what a stack allocation is it's extremely fast let's take a look at what the heap allocation actually does so there's not really much point in showing you this because the memory is not really gonna be close together but if we just take a look at H value you see we've we still of course get five over here and then just in the kind of middle of nowhere if you note the address of this it's actually a 59 B b1 8 now if I look at my array and I take a look at H array you can see it's at a completely different memory address he hasn't been initialized yet so if we had F 10 a few times you can see of course those variables do get set but that address is 5 6 8 they're at the end and H value is B b1 8 just completely different areas of our memory so there's not really much point in showing you this memory of you anyway but what I do want to mention is a few things about this heap allocation first of all yes I am using the new keyword here however if you were using smart pointers and you were using make unique or make shared it's exactly the same thing it will call new for you and then the other really important thing of course is that you actually have to delete memory deallocate using new smart pointers will do that for you but since we have used new here we actually have to call delete H value delete HRA with the array delete operator here and delete H vector as well so we do actually have to manually free our memory whereas with the stack as well what happens is once this scope in which you've allocated that stack memory actually ends all of the memory that you've allocated in that stack just gets popped off it just gets freed so if we were to do something like a wrap this in a scope it can be any scope it can be this function scope of this main function or it can just be an empty scope like this or like a for loop or a while loop of an if statement whatever any kind of scope when this scope comes to an end everything that was allocated on the stack inside that scope just gets popped off it's just free to just go on its reclaimed because the stack just moves to the position it was before we actually entered this scope so that's another really important difference between the two the stack you kind of just get a free free or in the sense that it doesn't cost you anything to free any memory because the stack just because again freeing memory with the stack is basically the same operation as allocating it's just that instead of moving our stack pointer backwards and returning that address we just pop everything off the stack so our stack pointer goes back up to where it was before the scope began and once if you instruction it's basically free whereas with delete that obviously has to actually free everything now let's talk a little bit about what the new key what actually does now I do have a video on this definitely check that out if you're more interested in this kind of object lifetime stuff as well I have made a video about that as well so the object lifetime video is more about how stack allocations work and how we can kind of you know use scopes to our advantage when dealing with this automatic deletion thing definitely recommend checking it out as well in fact I'll probably just leave a list of videos over there in the top right corner so that you can just check out anyway let's just talk a little bit about how the heap actually works and what new and delete do so the new keyword will really just call malloc a function called malloc or memory allocate and what that will do is in turn usually call the underlying operating system like platform specific function and that will allocate memory for you on the heap and the way that it does that is when you say application you get a certain amount of physical RAM kind of allocated to you and your program will maintain something called a free list which basically just keeps track of which blocks of memory are free and where they are and all that so that when you actually ask for dynamic memory zhing by dynamic memory a memory when you ask for heap memory use Mallett it will be able to kind of go through the free list and be like oh yeah I have a free block of memory that is at least as big as what you've asked for I will give you a pointer to that and then I'll also record some things such as the size of the allocation and the fact that it's now allocated and you can't use that block of memory anymore there's a bunch of bookkeeping that goes on and then you basically get that pointer back now the actual implementation of malloc is kind of dependent on the implementation right I have linked a few I've dropped some links in the description if you want to know more about exactly what malloc doesn't how it works it's a fairly heavy function there's a lot of bookkeeping that needs to be done and you don't just get your memory and to make things even worse if you've asked for more memory than is actually in that free list in that initial kind of allocation that your operating system is giving you then your application your program has to actually ask your operating system hey I need some more memory please and that is very expensive so there's that potential cost which is huge really the point that I'm trying to make here is that allocating memory on the heap is a whole thing whereas allocating memory on the stack is like one CPU instruction that is all I want you to take away from this video really the fact that the differences between these two are primarily the allocation now you could argue that the other benefit of allocating memory on the stack or just storing variables in the stack is the fact that they're close together in memory and therefore they can basically fit onto one kind of CPU cache line if we're looking at the actual code that we've got here though I mean we're talking about using the heap potentially like a couple of cache misses verses in the stack maybe we wouldn't get any after we kind of request that first stack variable we put something on to the stack a couple of cache misses versus no cache misses is like no big deal at all you probably won't notice the difference at all if we're dealing with a couple of million of cache misses right a couple million cache misses that's a big deal but with a few cache misses like if it's kind of you can't really argue the point the the fact that the memory is closer together on the stack or the fact that the stack is probably hot because you're continually accessing is just just by keeping like local bear there and also putting stuff into registers for when you pop functions onto the stack and stuff like that that's kind of a valid point but in the real world and you probably won't notice the difference because you're just not getting enough cache misses for it to actually make first actually cause a problem so the difference the big difference between the two is the allocation the allocation is the slow part allocating memory on the stack is once if your instruction in fact let's take a look at the generators assembly behind what we've just written and we can see what it actually does I'm just going to compile this code by hitting ctrl f7 in my properties from my project I've also just got under CC Plus last output files I've got assembler output to assembly with source code so that we can look at it and here we have the assembly so let's just go down to our value variable okay so here's our code int value equals 5 that is the CPU instruction that it runs now this is compiled and debug won't keep that in mind so there may be some extra things when it comes to the heat comparison but the stack you can see all the does is it moves five into a register that's it done or specifically into this kind of stack pointer add a specific offset but the idea is that's it it's once a few instruction now with the array we kind of have two things here and it actually does a bit of a multiplication whatever I'm not going to talk about the semantics of that but you can see what it's really just done is it's just allocated enough space for our kind of array here and that's it you can with I mean this is just setting variables whatever and with our vector allocation again it's called a constructor over here but other than that the allocation is actually just immediate now here is our allocation on the heap look at what that does the main thing is it calls an entire operator and that operate a new course malloc and then that obviously has to go through the free list and check to see if we've got enough memory and get it the memory and record the fact that it's now been taken and how much has been allocated and then we have to delete it off - we're done the whole nightmare right and if we keep reading you can see the same thing obviously happens for the array and then finally if we look at even deleting the vector first of all it is quite heavy and it does pull the constructor of course the same as our stack the delete is incredibly heavy as well keep in mind this is compiled in debug mode which is why you're seeing so much code it would be a lot leaner in release mode but still there's going on here and then of course with a regulating and linear vector we get a bunch of stuff as well but again main point being that allocating that value v on the stack is just that that's all it is right and of course if this was a class that just had a bunch of integers it would look the same the same essentially right I mean you can see that the vector allocation looks exactly the same it's just cause the constructor so I hope that all of that is kind of clear to you the fact that you should try and allocate on the stack whenever possible the only reason really to allocate on the heap is if you can't allocate on the staff whether whether you need that lifetime to actually be longer than the scope of your function or whatever scope you're dealing with or you specifically need more data like I want to load a texture that's 15 megabytes or something like that that's not gonna fit onto the stack you'll have to allocate that on the heap and all that kind of stuff right but if you can you should be allocated on the stack all the time because it's like once if you instruction and that's very very real performance difference now one more time just so that this is completely clear the performance difference is the allocation so if you were theoretically to pre-allocate I don't know a full gigabyte block of memory before you ran your program on the heap and then you were to kind of heap allocate from that pre-allocated for like gigabyte block of memory it would basically be the same right the only thing you're potentially dealing with there is again CPU cache misses but there's probably not enough of them to actually matter so the fact that when you call new it has to go through the free list and ask for memory and book keep all of that that is the slow part of the stack versus the heap the actual access is usually negligible right usually not always we might talk more about CPU cache optimization and stuff like that certainly if you're writing through a collection of a million elements and every single one of them is a cache miss you're gonna see a very real performance difference between if you have everything kind of contiguously or fragmented but that's a nother video drop a comment if you want to say that I think I'll make it because that's probably something is very interesting very interesting to a lot of people but anyway that's about it for this video we're definitely going to learn more about kind of allocating in the real world and how we can minimize allocations we'll have to spend quite a bit of time discussing allocations when we actually start out gaming into the series because it's very important for kind of real world applications oh and by real world applications between real time applications so it's really important for games essentially not to continuously allocate frame to frame because that will be slow so we have to basically come up with some clever memory management techniques if we want to our game engine to actually be efficient which is why we will definitely be discussing that in the game engine series anyway I hope I hope I've answered all of your kind of stack first heap questions if I didn't drop a comment below I'll maybe make a follow-up video if I need to I think that's about it there I mean really we didn't go to in depth into how it actually works on like an operating system level but I think for most people it's probably gonna be enough if you enjoyed this video please hit the like button you can also support this series and everything that I do here on YouTube by going to patronymic on course at the Turner huge shout out to all of my patrons there this video that you're saying right now probably wouldn't exist if it wasn't for them so again huge thank you and there's a bunch of really cool rewards that you did that you can actually get if you go out there and you help support this channel next time I don't even know what we're gonna talk about next time I've got a list but if you guys want to see something specific drop a comment below and just hit the thumbs up button on any comment that you like so that the kind of top rated comments kind of go up to the top and I'll see if I can make a video else cuz it'll be that because I want to make videos that you guys want to see I will see you next time goodbye [Music]
Info
Channel: The Cherno
Views: 331,719
Rating: undefined out of 5
Keywords: thecherno, thechernoproject, cherno, c++, programming, gamedev, game development, learn c++, c++ tutorial, stack, heap, memory, stack vs heap, memory allocation
Id: wJ1L2nSIV1s
Channel Id: undefined
Length: 19min 31sec (1171 seconds)
Published: Sun Dec 10 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.