Skip to content

Caught exception objects are never freed (throwable leaks once per catch) #448

Description

@mirchaemanuel

Summary

A caught exception object is never freed: each throw whose exception is handled by a catch leaks the throwable heap block (~48 bytes), once per catch. In a loop that throws-and-catches, the heap grows without bound.

Verified on

main (macOS ARM64, default EIR backend), via --heap-debug leak summary.

Minimal reproduction

<?php
for ($n = 0; $n < 200; $n++) {
    try {
        throw new TypeError("x");
    } catch (\Throwable $e) {
        // handled; $e goes out of scope at the end of the catch
    }
}
echo "done\n";

Actual (--heap-debug)

HEAP DEBUG: leak summary: live_blocks=200 live_bytes=9600

live_blocks grows linearly with the number of caught exceptions (N=50 → 50, N=200 → 200): the throwable allocated by throw is never released after the catch handler completes.

Expected

The caught exception object should be released when it leaves the catch scope (like any other scope-local object), leaving the heap flat across iterations.

Notes

General exception-handling ownership gap, independent of the throw source (a user throw new ..., a builtin fatal-to-TypeError, etc. all leak the same block). Not a crash — a steady per-catch leak. Long-running programs that use exceptions for control flow will accumulate memory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions