Memory Management

Updated: 2021-11-19

Stack vs Heap:

  • data on the stack must have known, fixed size at compile time
  • data with unknown size at compile time or size that might change must be stored on the heap
  • operational-wise (allocation, access) heap is more expensive (slower)
  • heap also requires bookkeeping (what part of code uses what data, deduplicating data, cleaning up unused data), which ownership addresses

Manual memory management (Explicit Deallocation)

The programmer need to explictly free the memory. The problems:

  • deallocate memory prematurely which creates a dangling pointer. Dangling pointers are pointers that no longer point to valid objects in memory.
  • If the programmer forgets to free an object they may face a memory leak as memory fills up with more and more objects. This can lead to the program slowing down or crashing if it runs out of memory.

C and C++ provides ways to manually allocate and deallocate heap memory.

  • C: Use functions malloc() / calloc() and free().
  • C++: Use keywords new and delete.

Memory Safety Without Garbage Collectors

C++: Smart Pointers

(TODO)

Rust: Ownership

Ownership is how Rust manages memory. It's a set of rules that the compiler checks at compile time which don't slow down the program while running.

Ownership Rules:

  • Each value in Rust has a variable that’s called its owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

Example:

fn main() {
  let a = vec![1,2,3];
  let b = a;
  println!("a: {:?}", b, a);
}

The compiler throws an error because a has already been dropped in the third line.

In comparison, languages with garbage collectors would run through in the second case. The garbage collector would drop A only after the last time that it is called, which is nice for the developer but not so nice in terms of memory space.

Another example:

let s1 = String::from("hello");
let s2 = s1;
// s1 is not valid anymore

// Alternatively
let s1 = String::from("hello");
let s2 = s1.clone();
// both, `s1` and `s2` are valid

Swift

Swift compiles to (native) machine code by default. It’s called ARC (Automatic Reference Counting). This approach is based on tracking the strong references count to an object held by other objects. as soon as the references counter goes to 0, the object will be reclaimed immediately*, without any pause and without initiating the GC collection cycle. It’s a big advantage over the tracing garbage collection type. garbage collection process on Android works in the runtime of your app, whereas ARC is provided at compile time.

To resolve the strong reference cycle, you should use a weak or unowned reference. You just add a special keyword before a variable, and then when you assign an object to that variable, the object’s references counter is not bumped up.

Using the weak keyword in iOS is something normal and can even be considered good practice when you widely use the Delegation pattern. When it comes to Android, it’s not a common practice

Due to the retain cycles, iOS developers sometimes need to write more complex code for simple things than Android developers.

Another problem is the Lapsed Listener problem. In short, when you register a listener and forget to unregister that, as a consequence you end up with a memory leak in your app. https://en.wikipedia.org/wiki/Lapsed_listener_problem

Garbage Collectors

Garbage collectors operates on the heap, not the stack.

Java and JVM Languages

Multiple garbage collectors provided, primarily:

  • G1 Garbage Collector: default GC since Java 9
  • Z garbage collector
  • Shenandoah Garbage Collector

Read more: Java Garbage Collection

C++23 remove garbage collection support

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2186r2.html

Examples of virtual machines written in C++ with support for garbage collection:

  • WebKit’s JavaScriptCore use a garbage collector called Riptide.
  • Chromium’s Blink GC called Oilpan.
  • The V8 JavaScript engine used by Chromium also has its own garbage collector called Orinoco.
  • Firefox’s SpiderMonkey JavaScript engine
  • Lua and LuaJIT

each garbage collector has its own set of design criteria which influence how the language itself is implemented. These languages use similar ideas, but the design is different in each case, and the constraints on C++ code are different.

Garbage Collection in C++ is clearly useful for particular applications. However, Garbage Collection as specified by the Standard is not useful for those applications. "