\subsubsection{Dynamic Memory Allocation}
Memory allocated with \texttt{malloc} is typically $8$- or $16$-byte aligned.

\content{Explicit vs. Implicit} In explicit memory management, the application does both the allocation \textit{and} deallocation memory,
whereas in implicit memory management, the application allocates the memory, but usually a \textit{Garbage Collector} (GC) frees it.

For some languages, like Rust, one would assume that it does implicit allocation, but Rust is a language using explicit management,
it's just that the \textit{compiler} and not the programmer decides when to allocate and when to deallocate.

\warn{Assumption in this course:} Memory is \bi{word} addressed (= 8 Bytes on 64-bit platform).

\content{Goals} The allocation should have the highest possible throughput and at the same time the best (i.e. lowest) possible memory utilization.
This however is usually conflicting, so we have to balance the two.

\inlinedef \bi{Aggregate payload} $P_k$: All \texttt{malloc}'d stuff minus all \texttt{free}'d stuff

\inlinedef \bi{Current heap size} $H_k$: Monotonically non-decreasing. Grows when \texttt{sbrk} system call is issued.

\inlinedef \bi{Peak memory utilization} $U_k = (\max_{i < k} P_i) / H_k$


A bit problem for the \texttt{free} function is to know how much memory to free without knowing the size of the to be freed block.
This is just one of many other implementation issues:
\begin{enumerate}[noitemsep, label=(\arabic*)]
    \item How much memory to free? $\rightarrow$ Headers
    \item How do we keep track of the free blocks? I.e. where and how large are they? $\rightarrow$ Free lists
    \item What do we do with the extra space of a block when allocating a smaller block? $\rightarrow$ Coalescing
    \item How do we pick a block? $\rightarrow$ Placement policies
    \item How do we reinsert a freed block into the heap? $\rightarrow$ When to coalesce
\end{enumerate}
This all leads to an issue known as \bi{fragmentation}

\inlinedef \bi{Internal Fragmentation}: If for a given block the payload (i.e. the requested size) is smaller than the block size.
This depends on the pattern of previous requests and is thus easy to measure

\inlinedef \bi{External Fragmentation}: There is enough aggregate heap memory, but there isn't a single large enough free block available
This depends on the pattern of future requests and is thus hard to measure.


\content{Header}: Stores size of block and is usually placed in the word that preceeds the allocated block (standard method).


\content{Free lists}
\rmvspace
\begin{enumerate}[noitemsep, label=M\arabic*]
    \item \bi{Implicit list} using length: Links all blocks and uses a low-order bit to indicate free / allocated,
          as for aligned blocks, a / some low-order bit(s) are always 0).
    \item \bi{Explicit list} among free blocks using a pointer in the first (and possibly second) word of the block
    \item \bi{Segregated free list}: different free lists for different size classes
    \item \bi{Blocks sorted by size}: Using a balanced tree with pointers within each free block and the length is the key
\end{enumerate}


\inlinedef \bi{Coalescing} Connecting two (or more) (free) blocks to form a larger (free) block.

We can do this efficiently in one direction with just a header, however in both directions requires what are referred to as \texttt{boundary tags}.
They are simply headers on both sides of the block and this allows us to traverse backwards.

We can do coalescing in constant time, by looking at the previous block's footer and next block's header to check if they are free or not.\\
\begin{itemize}
    \item If the previous block is free, we can coalesce it by updating its header to include the length of the to be freed block and the middle two words.
          Same update has to happen to the to be freed block's footer.
    \item If the next block is free, update its footer to the size of the to be freed block plus the free block's size plus the two words in the middle.
          Do the same update to the to be freed block's header.
\end{itemize}
If both blocks are free, then of course we can do this step in one go for both.

Using the headers or boundary tags is just one option to do it and it can be optimized.

\content{Implicit Free List} If we use the size that is stored in the header, we know where the next block is going to be already.
To know if a block is allocated, we use a low-order bit. This is possible, sincee if the blocks are aligned, then some of the low-order bits are always 0.

\textbf{Allocating Blocks}: We traverse the list, and choose depending on the placement policy: \texttt{first fit}, \texttt{next fit} or \texttt{best fit} policies.
When a block is picked, we might want to split it by adding a header to the remaining part.\\
\textbf{Freeing Blocks}: We can simply clear the allocated flag, which might lead to fragmentation. Thus: Coalesce the freed blocks.

This is the simplest approach, the price we pay is that allocating is linear w.r.t \textit{all blocks} managed by the allocator.

\content{Explicit Free List} To improve on this, here we maintain pointers to the next and preferably previous free block(s).
This means that all free blocks form a (doubly) linked list, and we don't traverse already allocated blocks.\\
Since the free blocks aren't used by the program, the list pointers can be stored inside the free blocks.

\textbf{Allocating Blocks}: We remove the element from the list by updating the pointers and
we can again use the \texttt{first fit}, \texttt{next fit} or \texttt{best fit} policies.\\
\textbf{Freeing Blocks}: Requires updating the list pointers, which depends on the list ordering. If boundary tags are used for coalescing, we also have to clear the allocated bit in the footer and header.

The list ordering may be LIFO (last-in-first-out) or address-ordered. LIFO (with \verb|first fit|) allows constant time freeing, while address-ordered provides better memory utilization (linear time freeing).

To prevent fragmentation, we want to use coalescing again, which can be implemented using boundary tags (again leads to linear time coelescing, if the list is FIFO). This time, the pointers need to be updated too. How this works (and how fast) depends on the list ordering.

\content{Segregated Free List} An issue with the previous approaches is finding a \textit{large enough} block, which is why finding a fit remains in linear time. Here, we keep separate lists for different \textit{size classes} to make this faster.\\ 
For each \textit{size class} a separate free list is maintained. For example, the classes might be:
$$
      \{1\}, \{2\}, \{3,4\}, \{5-8\},\ \ldots\ ,\{1025-2048\}, \{2049-4096\}, \{4097-\infty\} \quad\quad\quad (\text{Partitions by Powers of } 2)
$$

\textbf{Allocating Blocks}: We first check the list for the requested size class,
and if no fitting block is found, move up to the next list. If we have reached the last list and there is no suitable block, request new memory using \texttt{sbrk()}.\\
\textbf{Freeing Blocks}: Requires adding the newly freed block into the respective free list, potentially after coalescing.

This leads to an increased throughput and better memory utilization as compared to the previous two. Additionally, this structure opens up many optimization options.

The way splitting, coalescing, etc. is implemented varies a lot by implementation.\\
For example, we might choose to only coalesce if the length of a certain list has reached a certain threshold.\\
Similarly, if a list of some size class is empty, we might either request new memory and create a new list, or split a block from a higher size class.

Some options used are: \textit{Simple Segregated Storage}, \textit{Segregated Fits}, \textit{Buddy System}

\shade{purple}{Fun fact}: the GNU \verb|malloc| package uses a Segregated Free List, with \textit{Segregated fits}.

\content{Placement policies}
\begin{itemize}
    \item \bi{First fit} Search the list from the beginning, pick first free block that fits. This will usually cause ``splinters'' at the beginning of the list
          and can take linear time in the total number of blocks (allocated and free)
    \item \bi{Next fit} Like first fit, but start at point the previous search finished at. This should be faster, however leads to worse fragmentation.
    \item \bi{Best fit} Searches the list and chooses the \textit{best} free block that fits and has fewest bytes left over.
          Leads to lower fragmentation, but is slower than first fit.
\end{itemize}
