Category
Foundation: · runtime memory model · child of #36188
Problem
size -A build/audit/hello on master 4eed6a2785: .bss 69,876,800 B (23,765 BSS symbols) against .text 12.6 MB. The big ones:
| symbol |
bytes |
source |
phpc_stream_write_buffer_storage |
2,097,152 |
StreamGlobalsJit::GLOBAL_WRITE_BUFFER_STORAGE |
__phpc_ob_storage |
524,288 |
output buffering |
phpc_gc_objects |
524,288 |
GcCollectCyclesRuntime::MAX_OBJECTS = 65536 × 8 |
phpc_gc_prop_counts, phpc_gc_inbound |
262,144 each |
same registry |
phpc_gc_marked, phpc_destruct_invoked |
65,536 each |
same |
BSS is not file size, but it is committed address space and page faults on first touch, and — the real defect — the sizes are hard capacity limits:
phpc_gc_register (GcCollectCyclesRuntime.php ~L335-375):
$atMax = icmp sge count, MAX_OBJECTS
$skip = or objNull, atMax
branchIf $skip -> done ; object is silently never registered
After 65,536 live objects have been registered, every further object is invisible to gc_collect_cycles(); cycles among them leak for the life of the process with no diagnostic. A long-running phpc fcgi worker or any script building >64k objects hits this. Zend's GC buffer grows (gc_grow_root_buffer).
php-src reference
- Zend/zend_gc.c —
GC_DEFAULT_BUF_SIZE 16K roots, gc_grow_root_buffer() doubles up to GC_MAX_BUF_SIZE; the buffer is heap-allocated on first use.
PHP implementation target
GcCollectCyclesRuntime: replace the five fixed [MAX_OBJECTS x T] globals with a heap-allocated, growable registry (phpc_gc_buffer_size global already exists — use it): realloc on full, cap only at an explicit PHP_COMPILER_GC_MAX_ROOTS with a stderr warning when reached.
StreamGlobalsJit / OB storage: allocate lazily on first write (malloc in the first write), keep sizes as initial capacity, grow geometrically.
- Add
gc_status() parity (runs, collected, roots, buffer_size) so the change is observable.
Repro
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && cat > build/gc.php <<'"'"'EOP'"'"'
<?php class N { public $o; } for ($r = 0; $r < 3; $r++) { for ($i = 0; $i < 100000; $i++) { $a = new N; $b = new N; $a->o = $b; $b->o = $a; } echo gc_collect_cycles(), "\n"; }
EOP
php bin/compile.php -o build/gc build/gc.php && ./build/gc && php build/gc.php && size -A build/gc | grep bss'
Zend collects ~200,000 per round; today AOT collects at most 65,536 and .bss is 70 MB.
Done when
Category
Foundation:· runtime memory model · child of #36188Problem
size -A build/audit/helloon master4eed6a2785:.bss 69,876,800 B(23,765 BSS symbols) against.text 12.6 MB. The big ones:phpc_stream_write_buffer_storageStreamGlobalsJit::GLOBAL_WRITE_BUFFER_STORAGE__phpc_ob_storagephpc_gc_objectsGcCollectCyclesRuntime::MAX_OBJECTS = 65536× 8phpc_gc_prop_counts,phpc_gc_inboundphpc_gc_marked,phpc_destruct_invokedBSS is not file size, but it is committed address space and page faults on first touch, and — the real defect — the sizes are hard capacity limits:
phpc_gc_register(GcCollectCyclesRuntime.php ~L335-375):After 65,536 live objects have been registered, every further object is invisible to
gc_collect_cycles(); cycles among them leak for the life of the process with no diagnostic. A long-runningphpc fcgiworker or any script building >64k objects hits this. Zend's GC buffer grows (gc_grow_root_buffer).php-src reference
GC_DEFAULT_BUF_SIZE16K roots,gc_grow_root_buffer()doubles up toGC_MAX_BUF_SIZE; the buffer is heap-allocated on first use.PHP implementation target
GcCollectCyclesRuntime: replace the five fixed[MAX_OBJECTS x T]globals with a heap-allocated, growable registry (phpc_gc_buffer_sizeglobal already exists — use it):reallocon full, cap only at an explicitPHP_COMPILER_GC_MAX_ROOTSwith a stderr warning when reached.StreamGlobalsJit/ OB storage: allocate lazily on first write (mallocin the firstwrite), keep sizes as initial capacity, grow geometrically.gc_status()parity (runs,collected,roots,buffer_size) so the change is observable.Repro
Zend collects ~200,000 per round; today AOT collects at most 65,536 and .bss is 70 MB.
Done when
gc_collect_cyclessemantics); atest/differential/cases/case pins it.bssof hello-world < 4 MBMAX_OBJECTSarray remains;phpc fcgisoak (script/fastcgi-smoke.shwith 200k requests) shows flat RSS