Hey everyone, this time I propose to analyze the theoretical part and an equally interesting topic.
Let's talk about memory types in Swift. Let's start as we should from the very beginning, memory management is an important part of developing efficient applications, misusing it to problems ranging from slow loading to crashes.
There are three types of memory:
static
automatically
dynamic
A static type is responsible for allocating memory before the start of program execution; such memory is available throughout the entire program execution time in many languages. To allocate static memory objects, it is enough to declare its global scope.
Automatic type or stacking is the most basic. Arguments and local variables of the function are automatically allocated, as well as other meta information when the function is called, and memory is freed when it exits.
Dynamic type allocation of memory from the operating system as required by the application. The application can, if necessary, request additional memory from the operating system via the Locator or directly via a system call. An locator is a part of a program that requests memory in large chunks directly from the operating system through system calls, then gives this memory to the application piece by piece.
How is RAM used when running a program? When executing a program, it needs access to RAM to load its machine code for execution, store the value of variables and data structure, and load external modules needed to perform tasks. In addition to the place used to load its own code, the application uses two areas of RAM during operation, the stack on the basis of which automatic memory works and the so-called Heap, which is used for dynamic memory allocation.
Now let's talk more about these mysterious stacks and heaps.
So let's start with stacks.
The allocation of memory for the stack is determined when the program is compiled, access to it is very fast. The stack is organized in a Last In First Out (LIFO).
The stack can be visualized as a stack of books with which we are only allowed to interact with the topmost book, read it and put on a new one. All manipulations are performed with the top book in the stack, the book is added to the very top if you need to save the data, or taken from the top if the data needs to be read.
There are two main commands for working with stacks.
Push pushes data to the top of our stack of books and Pop gets the book from the top of the stack. Each function on the stack has its own place called a frame. When one function calls another, the latter always knows where to get its arguments at the end of the stack.
How does the function know where the end of the stack is? The processor has a special register for this, which finally stores the stack pointers and in most cases it is located closer to the end of the virtual memory and grows towards the beginning. Each thread of a multithreaded application has access to its own stack.
Stacks are used for value types, that is, for value types. If the size of a value type can be determined at compile time or if our value type does not contain a recursion of itself or is in a reference type then it will require stack allocation.
Heaps are used to dynamically allocate memory, you can think of a heap as a big bookcase where a needed book can be found using a specific instruction. After allocating memory, the program receives a pointer to the beginning of the allocated memory, which, in turn, must also be stored somewhere, in static, automatic, or also dynamic memory.
Ultra-high-level languages use dynamic memory as the main one. All or almost all objects are created in dynamic memory, and a pointer to these objects is kept on the stack or in static memory. Operations on the heap are somewhat slower than on the stack, since it requires an additional step to look up the data.
The heap is shared by all threads in an application due to its dynamic nature. The heap is more difficult to manage and causes most of all memory-related problems and errors. The heap is used for reference types.
When does heap partitioning happen differently? One option for when the Swift compiler will promote reference types to be placed on the stack is when their size is fixed or the lifetime can be predicted.
Also, the Swift compiler can box value types and allocate them on heaps in the following cases:
By following the protocol. In addition to resource allocation costs, additional memory leaks occur when the value type is stored in an existential container and exceeds three machine words in length.
An existential container is a generic container for a value of an unknown runtime type, small value types can be inlined in an existential container, larger ones are placed on the heap and referenced in the container's existential buffer. Also the second option when mixing a value type and a reference type. A regular reference to a class is stored in a struct, and a struct is a field of the class.
The third case is escaping closures. In the case where a value is stored on the stack and is captured by an escaping closure, then that value is copied to the heap so that it becomes available before the closure is executed.
And one more option is the inout parameter.
The inout parameters are passed to the entry point at address, the callee does not take ownership of the specified memory, the specified memory must be initialized when the function enters and exits.
So, we got acquainted with the types of memory and let's sum up some results.
Stacks store temporary data, method parameters and local variables. Each time we call a method on the stack, a new piece of memory is allocated, this memory is freed when the method exits. Stacks are specific to value types. Heaps store objects that have a lifetime. The heap is specific to reference types. The overhead of allocating and deallocating memory on the heap is much greater than allocating memory on the stack.
That's all I hope you liked this theoretical material, that's all.
Top comments (0)