logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector"); $this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings); } private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{ //TODO Very simple heuristic for dynamic GC buffer resizing: //If there are "too few" collections, increase the collection threshold //by a fixed step //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14 if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){ $this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP); }elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){ $this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP); } } public function getThreshold() : int{ return $this->threshold; } public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; } public function maybeCollectCycles() : int{ $rootsBefore = gc_status()["roots"]; if($rootsBefore < $this->threshold){ return 0; } $this->timings->startTiming(); $start = hrtime(true); $cycles = gc_collect_cycles(); $end = hrtime(true); $rootsAfter = gc_status()["roots"]; $this->adjustGcThreshold($cycles, $rootsAfter); $this->timings->stopTiming(); $time = $end - $start; $this->collectionTimeTotalNs += $time; $this->runs++; $this->logger->info(sprintf( "Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms", $this->runs, number_format($time / 1_000_000, 2), $rootsBefore, $rootsAfter, $cycles, number_format($this->collectionTimeTotalNs / 1_000_000, 2) )); return $cycles; } }