ddonche/goblin-lang
0.46.24
1
0
docs concept
[[memory-introspection]]

Memory Introspection


Goblin exposes its memory model through a set of introspection tools that let you observe stashes, tethers, and addresses in real time.

Note
Most of these tools are not yet implemented. :memaddr is the only one currently available. This page documents the full planned set.

:memaddr(x)

Available now.

Returns the memory address of the stash that x is tethered to.

x | [:a :b :c]
:memaddr(x)    /// -> "0x5555555a1040"
x | [:a :b :c]
:memaddr(x)    /// -> "0x5555555a1040"

Use this to confirm whether two names share the same stash or point to different ones:

x | 5
y | x

:memaddr(x)    /// -> "0x55555555f2c0"
:memaddr(y)    /// -> "0x55555555f2c0"  (same stash)

x |= 6

:memaddr(x)    /// -> "0x55555555f2e0"  (moved)
:memaddr(y)    /// -> "0x55555555f2c0"  (unchanged)
x | 5
y | x

:memaddr(x)    /// -> "0x55555555f2c0"
:memaddr(y)    /// -> "0x55555555f2c0"  (same stash)

x |= 6

:memaddr(x)    /// -> "0x55555555f2e0"  (moved)
:memaddr(y)    /// -> "0x55555555f2c0"  (unchanged)


:same_place?(x, y)

Planned.

Returns true if two tethers point to the same stash.

y | x
:same_place?(x, y)    /// -> true

x |= 6
:same_place?(x, y)    /// -> false
y | x
:same_place?(x, y)    /// -> true

x |= 6
:same_place?(x, y)    /// -> false


:tether_count(x)

Planned.

Returns how many tethers point to the same stash as x.

x | [:a :b :c]
:tether_count(x)    /// -> 1

y | x
:tether_count(x)    /// -> 2
x | [:a :b :c]
:tether_count(x)    /// -> 1

y | x
:tether_count(x)    /// -> 2

Also accepts an address string directly:

addr | :memaddr(x)
:tether_count(addr)    /// -> 2
addr | :memaddr(x)
:tether_count(addr)    /// -> 2


:memsize(x)

Planned.

Returns how much memory the stash uses in bytes.

items | [:a :b :c]
:memsize(items)    /// -> 48
items | [:a :b :c]
:memsize(items)    /// -> 48


:meminfo(x)

Planned.

Full diagnostic dump of a stash.

items | [:a :b :c]
:meminfo(items)
/// {
///   address: "0x5555555a1040"
///   type: "array"
///   size_bytes: 48
///   tether_count: 1
///   burned: false
/// }
items | [:a :b :c]
:meminfo(items)
/// {
///   address: "0x5555555a1040"
///   type: "array"
///   size_bytes: 48
///   tether_count: 1
///   burned: false
/// }


:list_untethered()

Planned.

Returns both abandoned stashes and untethered variables — the complete picture of what is disconnected in the current session.

:list_untethered()
/// {
///   stashes: [
///     { addr: "0x1000", type: "int", size_bytes: 8, preview: "5" }
///     { addr: "0x2000", type: "array", size_bytes: 48, preview: "[:data]" }
///   ]
///   vars: ["y"]
/// }
:list_untethered()
/// {
///   stashes: [
///     { addr: "0x1000", type: "int", size_bytes: 8, preview: "5" }
///     { addr: "0x2000", type: "array", size_bytes: 48, preview: "[:data]" }
///   ]
///   vars: ["y"]
/// }

Abandoned stashes have no tethers — the garbage collector will reclaim them. Untethered variables have no stash — they cannot be used until retethered.


:memory_snapshot()

Planned.

Returns overall memory usage for the current session.

:memory_snapshot()
/// {
///   total_allocated: 1048576
///   total_tethered: 524288
///   total_untethered: 524288
///   stash_count_tethered: 120
///   stash_count_abandoned: 80
///   gc_last_run: 1500
///   stashes_by_type: {
///     array:  { count: 45, bytes: 102400 }
///     map:    { count: 30, bytes: 204800 }
///     string: { count: 60, bytes: 61440 }
///     int:    { count: 40, bytes: 960 }
///   }
/// }
:memory_snapshot()
/// {
///   total_allocated: 1048576
///   total_tethered: 524288
///   total_untethered: 524288
///   stash_count_tethered: 120
///   stash_count_abandoned: 80
///   gc_last_run: 1500
///   stashes_by_type: {
///     array:  { count: 45, bytes: 102400 }
///     map:    { count: 30, bytes: 204800 }
///     string: { count: 60, bytes: 61440 }
///     int:    { count: 40, bytes: 960 }
///   }
/// }

Use before and after a section of code to observe memory behavior:

before | :memory_snapshot()

/// ... your code ...

after | :memory_snapshot()
say "Abandoned stashes created: " ++ after{"stash_count_abandoned"}
before | :memory_snapshot()

/// ... your code ...

after | :memory_snapshot()
say "Abandoned stashes created: " ++ after{"stash_count_abandoned"}