Heap & Stack: What the heck?
On interviews I was frequently asked the question: “Where the Swift Struct is being placed — Heap or Stack?” I always answered that it is placed in the Stack, because it is a Value Type. But I never really understood what Heap and Stack are, even though I had read a lot of articles explaining those concepts.
All explanations about Heap and Stack seemed too abstract to me, too detached from practical point of view.
Now, what if I tell you that there is a way to actually see the Heap in Xcode?
Memory Graph
At first turn on the checkbox “Malloc Stack Logging”. You can find it this way: Edit Scheme → Run → Diagnostics → Malloc Stack Logging.
Put a breakpoint in your code and run the Project. When you hit the breakpoint, click the “Debug Memory Graph” button.
After that you will see the Memory Graph where you can actually see what is in the Heap and what is in the Stack at real time!
Stack Trace
The things that are located in the Stack Frame are local variables and parameters of your current function. So Stack Frame is the scope of a function. Here you can see all Value Types (structs, enums, tuples, collections, primitives).
Also you can see Reference Types (e.g. classes, closures) in the Stack Frame. Well, not Reference Types themselves, but their addresses on the Heap. On the screenshot “Heap & Stack in Xcode” you can see the addresses of objects underlined in the Stack Frame and in the Heap. So when you make some operations on Reference Type variables, keep in mind, that you are affecting objects in the Heap which may be shared across different parts of your App.
The Stack consists of Stack Frames. Stack Frames stack up on each other in LIFO order. Previous functions’ Stack Frames that called current function are available in the Stack Trace navigator.
Each Thread has its own Stack with values protected from access from other Threads. This way thread safety is ensured.
Memory
The Memory of iOS App is just a list of bytes with data allocated by concrete addresses. Those addresses are called Address Space.
Memory consists of:
1. Text — machine instructions produced by Swift compiler after translating your app’s code.
2. Data — static variables, constants and type metadata. All global data that needs an initial value when the program is started goes here.
3. Heap — objects that have a lifetime, it’s mainly used for the reference type variables with some value type exception.
4. Stack — method parameters and function’s local variables.
The Heap and Stack grow towards each other.
Real Example
Let’s take a look at the real example of how Memory works.
While execution the App started in the application(_:didFinishLaunchingWithOptions) and then called firstFunction(). The firstFunction() created the ReferenceTypeClass on the Heap and created the ValueTypeStruct on the Stack. After that moved to the secondFunction(). Stack Frames of those functions stacked up on each other.
The amount of memory for each Stack Frame is determined when the program is compiled. It is important for compiler to know the exact sizes of Types in Stack. The system reserves the amount of memory required for a Stack Frame and then fills the “placeholders” with actual data as it executes.
In our case the placeholders for ReferenceTypeClass and Int were reserved.
The placeholder for valueTypeInt was filled with 22.
And referenceType2 was filled with 0x7f9b33c393e0 — a reference to the object on the Heap.
Classes are Reference Types they have a dynamic nature (they are inheritable) and therefore can’t be put in the Stack. Classes are allocated on the Heap at run time and are managed by ARC. The dynamic memory allocation on the Heap takes a little bit more time than static allocation of Stack. The Stack works very fast — there is no reference counting.
Also we can see a reference to self in each Stack Frame. It has an address of AppDelegate on the Heap.
Having the address in the variable means that when you make changes on that variable you are changing the object on the Heap.
When the last function (secondFunction()), which hasn’t called any other function, will finish execution, its Stack Frame with all variables will stop existence and execution will return to a previous function (firstFunction()).
Conclusion
I hope I made it very clear for you what is Heap and what is Stack.
You can actually see the Heap if you go to the Memory Graph in Xcode.
Reference Types go to Heap, only their addresses are placed in Stack Frame, and Value Types go to Stack. Well, Swift Compiler has a lot of optimisations which make Reference Types and Value Types much more complicated, but it is out of scope of current article.
Stack consists of functions’ scopes (Stack Frames) which stack up on each other in LIFO order. Each Thread has its own Stack.
If you have any questions or concerns, welcome to the comments section.