You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Runtime: dropping an array or object to refcount 0 frees only the header — packed values, string-key nodes and property boxes are never freed or delref'd, so every container death leaks (lib/JIT/Builtin/Refcount.php) #36215
Refcount.php:440-497 (drop-to-zero): four opaque calls — phpc_destruct_try_invoke, phpc_destruct_delref_allowed, phpc_weakref_clear_object_typed, phpc_gc_unregister — then free(ptr) of the header only. Nothing frees a __hashtable__'s values array, its __strkey_node__/__objkey_node__ chains, or an __object__'s property boxes, and the contained values are never delref'd, so nested strings/arrays/objects leak transitively. In {main} nothing is freed at all until shutdown by design (phpc_destruct_delref_allowed() returns 0, :458-480, #4013).
A phpc fcgi worker or any loop that builds and discards arrays grows without bound; the compliance/differential harnesses never see it because each case is one short process. The VM has a Refcount + COW model that does release (lib/VM/HashTable.php), so the semantics to mirror exist in-tree.
php-src reference
Zend/zend_variables.crc_dtor_func → zend_array_destroy / zend_objects_store_del: destroying a container delrefs every element, then frees buckets, then the header.
PHP implementation target
Emit __hashtable__dtor(ht): delref each packed value, walk and free strKeys/objKeys (delref key strings and values), free values; __object__dtor(obj): delref each property box, free slots. Dispatch on typeinfo kind at Refcount.php:495 before free.
Decide {main} policy explicitly: keep "no frees in main" only for globals that outlive the script; temporaries and locals in main must be released (or document why not and measure the cost on the examples).
Category
Foundation:· memory model · child of #36188Problem
Refcount.php:440-497(drop-to-zero): four opaque calls —phpc_destruct_try_invoke,phpc_destruct_delref_allowed,phpc_weakref_clear_object_typed,phpc_gc_unregister— thenfree(ptr)of the header only. Nothing frees a__hashtable__'svaluesarray, its__strkey_node__/__objkey_node__chains, or an__object__'s property boxes, and the contained values are never delref'd, so nested strings/arrays/objects leak transitively. In{main}nothing is freed at all until shutdown by design (phpc_destruct_delref_allowed()returns 0, :458-480, #4013).A
phpc fcgiworker or any loop that builds and discards arrays grows without bound; the compliance/differential harnesses never see it because each case is one short process. The VM has aRefcount+ COW model that does release (lib/VM/HashTable.php), so the semantics to mirror exist in-tree.php-src reference
rc_dtor_func→zend_array_destroy/zend_objects_store_del: destroying a container delrefs every element, then frees buckets, then the header.PHP implementation target
__hashtable__dtor(ht): delref each packed value, walk and freestrKeys/objKeys(delref key strings and values), freevalues;__object__dtor(obj): delref each property box, free slots. Dispatch ontypeinfokind atRefcount.php:495beforefree.{main}policy explicitly: keep "no frees in main" only for globals that outlive the script; temporaries and locals in main must be released (or document why not and measure the cost on the examples).valgrind --leak-check=fulland ASan (PHP_COMPILER_ASAN=1link flag) on one differential sweep; add--repeat 10runs — ownership changes are where heap corruption hides (AOT: script-scope ++/-- goes wrong once several statements or variables are involved (loop after statements, second variable) #23842).Repro
Expected: flat RSS (a few MB); today RSS grows with
$i.Done when
valgrindreports no definitely-lost blocks onscript/aot-smoke.shcasesscript/differential-sweep.sh --aot --repeat 10unchanged;examples-fastcgiweb-smokesoak of 10k requests shows flat RSSdocs/runtime-semantics.mddocuments ownership rules for containers and{main}