Question:
Everyone knows that with Stack this is a certain piece of memory that is allocated to each thread in the form of a size of 1MB , it stores references (ObjRef) to ReferenceType , user structures, primitive data, and local variables of the method.
And now the question is: what does the CLR do when the Stack is completely full, and, accordingly, there is essentially nothing to delete. That is, the Stack is full?
Can the CLR expand its bounds from 1MB to 2MB? (Or is it not possible, so we'll just get an Exception that will notify us of a stack overflow).
Another question: in the context of unsafe code, unamanged memory is also allocated 1MB, or can a custom size be allocated?
Answer:
To begin with, it is worth noting that the stack at the time of code execution is not some kind of abstract safe mechanism. .NET uses JIT compilation, so platform-specific code is actually executed using that platform's stack mechanism. In the case of x86, a stack segment + a pair of SS / ESP registers and push / pop operations. No separate stack is created for unsafe.
What happens when the stack is full and can it be increased when the limit is reached? No, in general, you can't.
The fact is that the stack, at least in x86/64, is a data structure that is filled from the end. Those. each push of something on the stack moves its pointer closer to the beginning. This comes from ancient (pre-.net) times when memory was scarce and the standard memory sharing looked like this:
[код][данные-куча --> ....... пустое место....... <-- данные-стек]
Physical memory was divided between the heap and the stack, and the programmer always had a choice – to allocate more memory for something on the heap, or put more objects on the stack. In addition, this layout effectively eliminated the need to control the size of the heap separately, and separately – the stack. Because if a heap and a stack meet in it, then there is no memory left at all.
A lot has changed since then (although the layout above is still relevant on some microcontrollers). In x86, the stack is now usually allocated a separate segment. But it, according to tradition, is filled from the end. Those. each push operation reduces the value of SP by the size of what is put on the stack.
When ESP reaches 0, the processor throws an error. There is no mechanism for "additional memory allocation" on the stack in x86 – simply because it is impossible to "additionally allocate" memory to the beginning of the segment – and the processor does not dare to allocate memory at the end and shift all the data on the stack – this is too complicated an operation for it. The operating system could do this, but at least Windows on x86 doesn't.
On exit, when the stack pointer reaches 0, you get a StackOverflowException
in your .NET code. This is an indicator of reaching the limit of the "iron" stack, and not some tricky CLR stack.
It's worth noting that infinite recursion can also lead to a StackOverflowException
on x86, even if you don't declare local variables. The fact is that the x86 method call mechanism ( call
) stores the return address from the called method on the stack. And the return operator ( ret
) pops the address off the stack. Therefore, too deep a chain of calls clogs the stack.
Actually, that is why the call view window is called the Call Stack – the information on the return chain is stored only on the stack. And on x86 – in the same physical stack as function parameters and local variables.