45 private const GC_THRESHOLD_TRIGGER = 100;
46 private const GC_THRESHOLD_MAX = 1_000_000_000;
47 private const GC_THRESHOLD_DEFAULT = 10_001;
48 private const GC_THRESHOLD_STEP = 10_000;
50 private int $threshold = self::GC_THRESHOLD_DEFAULT;
51 private int $collectionTimeTotalNs = 0;
52 private int $runs = 0;
54 private \Logger $logger;
57 public function __construct(
62 $this->logger = new \PrefixedLogger($logger,
"Cyclic Garbage Collector");
63 $this->timings =
new TimingsHandler(
"Cyclic Garbage Collector", $parentTimings);
66 private function adjustGcThreshold(
int $cyclesCollected,
int $rootsAfterGC) :
void{
71 if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){
72 $this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP);
73 }elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){
74 $this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP);
78 public function getThreshold() :
int{
return $this->threshold; }
80 public function getCollectionTimeTotalNs() :
int{
return $this->collectionTimeTotalNs; }
82 public function maybeCollectCycles() :
int{
83 $rootsBefore = gc_status()[
"roots"];
84 if($rootsBefore < $this->threshold){
88 $this->timings->startTiming();
90 $start = hrtime(
true);
91 $cycles = gc_collect_cycles();
94 $rootsAfter = gc_status()[
"roots"];
95 $this->adjustGcThreshold($cycles, $rootsAfter);
97 $this->timings->stopTiming();
99 $time = $end - $start;
100 $this->collectionTimeTotalNs += $time;
102 $this->logger->info(sprintf(
103 "Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
105 number_format($time / 1_000_000, 2),
109 number_format($this->collectionTimeTotalNs / 1_000_000, 2)