Understand how OCaml represents values in memory β boxed vs unboxed, stack vs heap, and garbage collection
OCaml uses a uniform representation: every value is either an unboxed immediate (fits in a machine word) or a boxed pointer to a heap-allocated block.
Type an OCaml expression and see its memory representation.
OCaml uses a generational GC with a minor heap (copying) and major heap (mark-sweep-compact). Watch it in action!
| Type | Representation | Size (words) | Notes |
|---|---|---|---|
int | Immediate (tagged) | 0 (in-register) | 63-bit on 64-bit systems, tag bit stolen |
bool | Immediate | 0 | false=0, true=1 (tagged) |
char | Immediate | 0 | Code point as tagged int |
unit | Immediate | 0 | Represented as 0 |
float | Boxed (Double_tag) | 1 header + 1 data | 64-bit IEEE 754 |
string | Boxed (String_tag) | 1 header + βlen/8β | Includes padding byte |
'a * 'b | Boxed block (tag 0) | 1 header + n fields | Same as records |
'a list | Cons cells (tag 0) | 1 header + 2 per cons | [] is immediate 0 |
'a option | None=immediate 0 | Some: 1 header + 1 | None costs nothing |
'a array | Boxed block (tag 0) | 1 header + n elems | Mutable, bounds-checked |
float array | Flat (Double_array) | 1 header + nΓ1 | Unboxed floats! Fast |
'a ref | 1-field mutable block | 1 header + 1 | {mutable contents: 'a} |
closure | Closure_tag | 1 hdr + 1 code + env | Env = captured vars |
| Constant variant | Immediate | 0 | Numbered 0,1,2... |
| Non-const variant | Boxed block | 1 header + n fields | Tag = constructor index |
| Property | Minor Heap | Major Heap |
|---|---|---|
| Strategy | Copying (semi-space) | Mark-sweep-compact |
| Default size | 256 KB | Grows as needed |
| Speed | Very fast (Β΅s) | Slower (ms) |
| When triggered | Minor heap full | Major heap grows past threshold |
| Objects survive | Promoted to major | Stay until unreachable |
| Allocation | Bump pointer (fast!) | Free-list based |
| Tip | Why |
|---|---|
Use int over float when possible | Unboxed = no allocation |
Use float array for numeric work | Flat layout, no per-element boxing |
Prefer Array over List for random access | Contiguous memory, O(1) access |
Avoid excessive ref usage | Each ref = heap allocation |
Use [@unboxed] for single-field records | Eliminates wrapper allocation |
Reuse buffers with Buffer.t | Avoids repeated string allocations |
Profile with Gc.stat() | Shows allocation rates and GC frequency |