PocketMine-MP 5.23.3 git-976fc63567edab7a6fb6aeae739f43cf9fe57de4
Loading...
Searching...
No Matches
GarbageCollectorManager.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine;
25
27use function gc_collect_cycles;
28use function gc_disable;
29use function gc_status;
30use function hrtime;
31use function max;
32use function min;
33use function number_format;
34
42 //TODO: These values could be adjusted to better suit PM, but for now we just want to mirror PHP GC to minimize
43 //behavioural changes.
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;
48
49 private int $threshold = self::GC_THRESHOLD_DEFAULT;
50 private int $collectionTimeTotalNs = 0;
51
52 private \Logger $logger;
53 private TimingsHandler $timings;
54
55 public function __construct(
56 \Logger $logger,
57 ?TimingsHandler $parentTimings,
58 ){
59 gc_disable();
60 $this->logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector");
61 $this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings);
62 }
63
64 private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{
65 //TODO Very simple heuristic for dynamic GC buffer resizing:
66 //If there are "too few" collections, increase the collection threshold
67 //by a fixed step
68 //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14
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);
73 }
74 }
75
76 public function getThreshold() : int{ return $this->threshold; }
77
78 public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; }
79
80 public function maybeCollectCycles() : int{
81 $rootsBefore = gc_status()["roots"];
82 if($rootsBefore < $this->threshold){
83 return 0;
84 }
85
86 $this->timings->startTiming();
87
88 $start = hrtime(true);
89 $cycles = gc_collect_cycles();
90 $end = hrtime(true);
91
92 $rootsAfter = gc_status()["roots"];
93 $this->adjustGcThreshold($cycles, $rootsAfter);
94
95 $this->timings->stopTiming();
96
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");
100
101 return $cycles;
102 }
103}