[Autogenerated] let's examine the differences between committed and
reserved memory in a more concrete concrete way.
We know that member manager uses reserved memory in order
to increase the stack size of a thread based on the actual
usage of stack for the thread.
But what other possible uses there could be there be for reserve memory.
So here's an example suppose I want to build
something which is like Microsoft Excel.
So essentially we have a big bunch of cells that the user can
use to enter data for whatever purpose.
So let's say that I would Like to provide some kind of
sizes for these matrix of sales.
So let's use two variables here.
I'm going to use 1,024 essentially allowing the user
roughly million cells to work with.
And so I don't want to limit the user.
On the other hand,
I would like to have easy access to that memory when I
need to make changes or read data.
Let's assume again,
just for demonstration purposes that every one of these
cells could hold up to one kilobyte of data.
So how can I very easily manage this memory?
One way of doing that is by using something like the Malek function to
allocate enough memory to cater for all these cells so I can do
something like sell X times cell Y time cell size.
We can allocate that memory and then we can very easily
calculate the address of particular cell based on what the
user needs and then perform the access.
Would that work?
Yes, that would work.
However we're committing Mallow can only commit memory.
it knows nothing else.
We're committing one gigabyte of memory up front and it's
not really clear whether the user will actually utilize all
that one gigabyte of memory.
In fact in most cases it probably wouldn't.
So that would be fairly wasteful.
What would happen if I want to increase the size of
the matrix to even bigger sizes?
If I commit all that memory up front,
I'm going to waste a lot of memories at some point I won't even be
able to do that because I'll run out of memory.
Remember that commitment requires the system to guarantee that
will be available to you whenever you need it.
And so it has to be guaranteed up front.
Now here's another suggestion maybe we should use reserve
memory instead of committing everything.
Let's just reserve that chunk of address space.
Of course there's nothing there at that point.
And so reserving is going to be very cheap operation.
And then when user wants to access a particular cell work,
commit just that cell or to be more precise the page where that
cell happens to fall in and then make the access.
We only commit four kilobytes,
that's it for any particular cell or a bunch of adjacent cells.
So with that scheme we're not wasting memory.
And so we're only using as much memory as we actually need based
on the actual usage of these cells by the user.
So let's see these differences when trying things out using
API So Malik is not really good here.
We want to use something more powerful called virtual a lock
which allows us to to perform various memory locations
including using reserved memory.
We'll discuss some more details about these API differences between that
and Malek and other Windows APIs that are provided for memory
manipulation in a future model later in this course.
So the first parameter to virtual a lock is the target address in
this case now means I would like to get some range of addresses
that is available in the process.
I don't really care what that address should be.
I don't care just find me something that is available and then we have the
size which we calculated in this way and then to provide the kind of memory
we'd like to actually allocate hero or reserve here really depends on whether
we want to commit or reserve memory.
So in my first example I'm going to commit the memory and technically you
have to use two flags here man commit and memory reserve.
Now technically again you can also use just men commit.
However this is not the right thing to do and because
Microsoft had a bug in the past that only that used men
commit as a replacement for men committed.
Men reserve, that would work.
But then because people were relying on that bug Microsoft
can never fix it but the correct thing to do is to reserve
and commit at the same time,
you can't really commit anything unless it was reserved first,
but you can do that at the same time by using these two flags essentially
want to commit this chunk of memory and I'm going to specify a read write
access for the return memory once I have that,
let's write something to that memory simply by using a simple string copy.
So let's just use something like hello memory or something like that.
And just use um very simple things like string copy just copying into buffer.
Let me just cast that here to make it easier to to work with
that memory without having to sweat a lot.
Let's just write something into the first cell,
the first cell of these matrix of cells,
someone to copy into buffer, whatever is in text.
But because that's the safe function here,
I'm not going to use the old functions string copy like that,
that's going to give me a deprecation or anyway and so we need to
provide the maximum size we can and we need here,
that's going to be the well really size of text is is actually good
enough for this purpose but it's not really the point.
The point is that if I commit memory, this access is bound to succeed,
there's no way in ____ this could fail.
So let's see the effect of that by placing a breakpoint and pressing a
five just before we perform the actual operation.
Well actually the breakpoint was not where I intended it to be.
So here's where I want that breakpoint to be.
So let's before we look at that, let's look at task manager,
see what it has to say.
In terms of memory,
I'm going to go to the performance area here and switch to memory.
You can see what's going on in terms of memory by
looking at the counters at the bottom.
So first we see we have 6.8GB of memory committed at this point
and after the slash we have the commit limit,
how much more can commit,
how much maximum memory can commit before perhaps
page file expansion will take place,
which would allow me to commit perhaps even more memory now 6.8 is
the committed size but in Iran we only have 6.1,
which means that some of that memory is elsewhere,
not necessarily physical memory right now.
So once I performed this virtual I'll call with memory committee,
what do I expect to happen?
What I expect to happen is to have the memory the committed
size jump by one gigabyte immediately,
even though I haven't touched that memory just yet.
This is going to jump by one gigabyte but the in use memory shouldn't really
budge at all because I don't need that memory just yet.
In terms of RAM, I just need the system to provide that commitment to me.
So here goes F 10 and we can see that the committed
memory has jumped to 7.8 immediately.
So now if I want to perform the string copying here,
that's not going to be a problem.
In fact,
let me add something to Visual Studio here and to
make it easier to watch this memory.
I'm going to go to the bug windows and their memory windows we here we
can use and I'm going to use the buffer address here to show the memory
and counting the memories all zero out.
And by the way, this is always the case with mutual a lock.
When you commit memory, the memory is always zero out.
This is guaranteed.
There's no need to zero it out.
If you think about doing that, please don't.
It's just a waste of time.
If I go ahead now and use string copy, we can see the memory appearing here,
we can see it changed as we expected and everything is is
great If I go ahead and end this application,
the memory will be released of course.
And now if I go back to task manager we can see we're
back in the 6.7 committed size.
So commitment is really the thing that we actually used to to access memory.
Now obviously if we change this number,
let's say I want to sell X to grow to uh let's say 10,240 cells in
the X direction and I try to do the same thing now,
Now I'm actually asking for the system to commit roughly 10 gigabytes of memory.
Let's see if that works out So notice nothing
happened and buffer is returned as now,
which means I couldn't perform this operation,
that was too much, that was too much for this particular system.
It only has 16 gigs of RAM and perhaps not a big enough page file to
accommodate all that memory for the 10GB that I needed here.
Well actually I made a mistake here.
That's not really the problem.
The problem is different.
It should have work that I do have enough memory,
I just have to make sure I perform the calculation here correctly because now
this number is beyond for gigabyte or 32-bit which means it's going to be uh the
calculation here is just going to truncate information so we have to change at
least one of those to something like long,
long,
which would be a 64-bit value and now the calculation would be done correctly.
So let's try this again, going back to task manager,
we can see 6.8, let's perform the allocation again and now it succeeds.
It works, we get back a proper pointer,
notice the committed memory jumped immediately by 10 gigabytes like immediately,
even though I haven't actually used that memory in
practice and noticed that the page file has to be had to
be resized and increased in size.
So now the commit limit has increased.
So I'm using more memory now.
So obviously the difference here Which is roughly 2.5 or 2.6 GB is
what's left for the other processes in the system,
noticed again,
physical memory hasn't changed at all because we're not really using
that memory in terms of physical access just yet.
If I perform now the operation of making that copy for
kilobytes would be provided in RAM.
But of course these counters are too big to show that small change in memory.
So that's the idea.
If I continue to increase that then at some point this will not work.
So if I do that I want to now commit 100 gigabyte of memory up front.
If I do that that actually fails, we get now inside buffer.
If I go to my watch window here and add the get last error
value and provide some textual description.
It says error commitment live.
The paging file is too small for this operation.
So the system try to kind of increase the page file size but the page file
cannot be increased so much in this particular system so we can't really commit
100 gigabytes of memory which of course makes sense.
And we know that we don't really want to work in this way
instead would like to change the way we allocate memory by
using reserve memory only.
So if I do that and I tried to reserve 100 gigabytes of memory
that works just fine buffer is returned with something noticed
the committed size didn't change, the RAM size didn't change.
However, if I look at buffer, here's what I get,
I get absolutely nothing and that's because there's no memory,
there's just reserved,
this is an address space that is now reserved for a particular purpose,
future purpose.
Of course, I'm the one determining that purpose.
So if I go ahead now I try to write to this location,
I'm going to get an exception,
I'm going to hit an exception of access violation and here goes,
you can see this exception saying,
hey uh whatever you're trying to do that's not going to pan out,
you have this access violation exception may be familiar with
this specific values C 000 many zeros and five,
that's an access violation.
So we're trying to access memory which is just not committed,
there's no way to do that and that just going to cause the process to _____.
So obviously that's not going to be good enough.
So what can we do to to solve that and to solve that?
What we need to do is to actually commit just the page
that we need in order to make the access,
so let's do that and because I'm using here the first page,
just for demonstration purposes,
that's going to be fairly easy to do and the way to do
that is to call virtual alec again,
But this time I want to request a particular address in
this case the beginning of this block,
which is buffer because I want to to commit the first cell so
the site is going to be cell size of course it's always going
to be rounded up to the next page boundary because everything
works in chunks of pages.
So I'm going to just ask for one cell.
But of course the API will commit that particular page
where that memory happens to fall.
Then men commit only this is the correct flag to use because the memory is
already reserved just a matter of committing just the pieces that I want to
actually access now and then I'm going to use Patriot right again because I
want to be able to write to this memory,
let's run this now and see what happens.
So first we perform the, the reserving of the memory and again,
just to prove that nothing interesting is happening here from
the perspective of any kind of wasting, not wasting anything.
If you look at buffer, we have absolutely nothing here, there's no memory.
This is visual studio's way of showing that there's no memory there.
And now when we commit the first cell,
which essentially is the first page going to be.
So it's actually the first four cells in practice because
every cell is one kilobyte in size.
In the in our simple example,
we should see all these question marks turning turning into zeros immediately.
That's it because that page is now committed and as I mentioned
when we have committed memory using virtual Alak,
it's always zeroed out.
We'll understand why this is important in the later module.
So now we can definitely go ahead and write the memory and we
get exactly the same results as before.
We can see here, the memory is working as expected.
And with this scheme we can definitely go ahead and change these
numbers to even crazier numbers like 100 gigabyte.
That's not going to be uh any problem here.
So it's actually a one terabyte now.
So one terabyte,
I can reserve that without Any problems because we have 128
terabytes address space in the process and reserving is very cheap
because it's not really accessible memory.
It just provides a range of addresses that's not going to be
used for a new allocation and then we can allocate or commit
just the pages that we need, which is going to be very efficient.
So our use of memory is very efficient and we're not going
to waste memory but still provide the user with the
flexibility to work with any cell.
The user wishes.
The last thing I want to demonstrate is another way to cater for this
particular case where we want to commit something.
So if you call future I'll a second time,
maybe the user tries to write again to the same cell.
Um that's not an error.
So committing an already committed page is fine but still it's kind of
wasteful because the cold will go into the kernel.
The kernel, we're going to look to the VC say, hey, that's already committed.
Why are you doing that again?
Okay, fine.
It succeeds.
It doesn't really do anything but this user to count transition is
completely a waste of time and the time that it takes the future I'll
KPI underneath within the kernel to figure out that in fact that page
is already committed so we can go about it in a different way by
leveraging exceptions,
exception handling something that we're not
discussing in this course explicitly.
But the example is fairly simple and the basic idea is
this let's try to access that memory.
If it works, it means the memory is already committed, then it will just work.
However if it doesn't,
we're going to get that access violation exception
and now we are able to fix it.
In fact by committing the page.
The appropriate memory that we need to commit and then
tell the process of please try again.
And so the way to do that is by using a try except block.
This is something that is known as structured,
exception handling mechanism Windows provides for handling exceptions.
This is something I discussed in one of the earlier Windows internal classes,
you might look at that up on Pluralsight but in any case we'll
see that this is not very difficult to write.
I'm going to call the function here called memory.
That's going to fix that memory.
I'm going to provide the exception code because if it's not an access
violation I have no idea what to do with that and then we need to provide the
address that failed to properly being access in this case that's going to be
buffer but in the general case could be any one of these addresses within
that big range of reserved memory.
So let's try this function and fixed memory has to return model
three possible values indicating how to proceed but for now
let's look at the exception code and the pointer we need to use
whatever that point may be.
The first thing I want to check is whether the code is not exception,
this is violation.
If it tax violation we want to take care of that.
If it's not then we have no idea what to do with that.
I'm going to return an exception code which is a value return from the
exception filter here called continue execution.
Sorry continue search which means I don't want to deal with that.
Please find some other handler potential handler for the exception
and if you can't find it then it's fine just _____ the process
because I'm only interested in only know how to handle an access
violation by the way this symbol is just the normal exclamation mark
it equals and just using a fund called cascadia code that provides
these nice ligatures.
That changes the way certain operators are displayed so don't worry the
c language hasn't changed while you didn't notice.
So now that we have an access violation on our hands,
all we need to do is really use the same kind of code we've seen here just
need to commit that particular piece of memory with whatever address we
received here and tell the processor to try again.
And this is done by returning a different constant called
continued execution saying I made some kind of fix,
please try this again.
So let's see if that works as we expect and uh and then here perhaps we
can just for fun um read information back from the the actual string here
by displaying whatever is in buffer at this point we expect to see the
string of course read back to us and you can put various breakpoints here
to see what that looks like.
Let's put a breakpoint here in fixed memory and run this.
So the first thing we're going to do is to reserve the big chunk of
memory and we know that at this point nothing should be here indeed
nothing is here but now we make the access we're going to get the
access violation now by default Visual Studio thinks that this is a
very severe exception.
And usually that's right.
And so it's going to stop even if we're handling that exception
unless we change that checkbox here saying that please don't break in
in that exception unless it's unhand aled.
And now we're going to press a five to continue execution and
we'll end up in this fixed memory call,
this is our call and looking at the code here,
which has this value, let me switch to hexadecimal just to see that C 0005 thing.
So obviously we do have an access violation.
The poynter P has the same value as the buffer here because
we're accessing the first cell in our example,
I'm going to commit the page and of course at that point
it will be zeroed out immediately.
Let me do that here goes so it's zeroed out and then we can continue running,
I can press a breakpoint here and the memory is going to
be written correctly as it should.
We can read that back.
If you want to look at the console here, it's right back, everything is fine.
So the next time we access the same cell of course
in a more complex application,
this will simply succeed without any hitch and will not be able
or not need to handle any kind of exception.
This will simply work.
So hopefully you get a better sense of the difference between
reserve and committed memory with reserve memory,
you create a bunch of address space range,
so really just a range of addresses you want to use for some future purpose
and then you can commit things in small chunks as required.
Only the committing part is the expensive part.
And and that's why we can allow ourselves here to
reserve one terabyte of address space.
I can even increase that to 10 terabytes of address space.
Really not a problem because we have 128 terabytes to begin with.
So 10 terabytes is really nothing.
And so we can reserve really enormous chunks of memory,
but again, there's nothing there.
But then you can commit them in stages as required and
the contingency of that address space, that's the important part.
We have the address space continuous in the case of an excel like application.
This is very useful because it would be very easy to to kind of jump
into a particular cell with a very simple calculation.