44 private const GC_THRESHOLD_TRIGGER = 100;
45 private const GC_THRESHOLD_MAX = 1_000_000_000;
46 private const GC_THRESHOLD_DEFAULT = 10_001;
47 private const GC_THRESHOLD_STEP = 10_000;
49 private int $threshold = self::GC_THRESHOLD_DEFAULT;
50 private int $collectionTimeTotalNs = 0;
52 private \Logger $logger;
55 public function __construct(
60 $this->logger = new \PrefixedLogger($logger,
"Cyclic Garbage Collector");
61 $this->timings =
new TimingsHandler(
"Cyclic Garbage Collector", $parentTimings);
64 private function adjustGcThreshold(
int $cyclesCollected,
int $rootsAfterGC) :
void{
69 if($cyclesCollected < self::GC_THRESHOLD_TRIGGER || $rootsAfterGC >= $this->threshold){
70 $this->threshold = min(self::GC_THRESHOLD_MAX, $this->threshold + self::GC_THRESHOLD_STEP);
71 }elseif($this->threshold > self::GC_THRESHOLD_DEFAULT){
72 $this->threshold = max(self::GC_THRESHOLD_DEFAULT, $this->threshold - self::GC_THRESHOLD_STEP);
76 public function getThreshold() :
int{
return $this->threshold; }
78 public function getCollectionTimeTotalNs() :
int{
return $this->collectionTimeTotalNs; }
80 public function maybeCollectCycles() :
int{
81 $rootsBefore = gc_status()[
"roots"];
82 if($rootsBefore < $this->threshold){
86 $this->timings->startTiming();
88 $start = hrtime(
true);
89 $cycles = gc_collect_cycles();
92 $rootsAfter = gc_status()[
"roots"];
93 $this->adjustGcThreshold($cycles, $rootsAfter);
95 $this->timings->stopTiming();
97 $time = $end - $start;
98 $this->collectionTimeTotalNs += $time;
99 $this->logger->debug(
"gc_collect_cycles: " . number_format($time) .
" ns ($rootsBefore -> $rootsAfter roots, $cycles cycles collected) - total GC time: " . number_format($this->collectionTimeTotalNs) .
" ns");