PocketMine-MP 5.27.1 git-9af3cde03fabbe4129c79e46dc87ffa0fff446e6
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;
34use function sprintf;
35
43 //TODO: These values could be adjusted to better suit PM, but for now we just want to mirror PHP GC to minimize
44 //behavioural changes.
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;
49
50 private int $threshold = self::GC_THRESHOLD_DEFAULT;
51 private int $collectionTimeTotalNs = 0;
52 private int $runs = 0;
53
54 private \Logger $logger;
55 private TimingsHandler $timings;
56
57 public function __construct(
58 \Logger $logger,
59 ?TimingsHandler $parentTimings,
60 ){
61 gc_disable();
62 $this->logger = new \PrefixedLogger($logger, "Cyclic Garbage Collector");
63 $this->timings = new TimingsHandler("Cyclic Garbage Collector", $parentTimings);
64 }
65
66 private function adjustGcThreshold(int $cyclesCollected, int $rootsAfterGC) : void{
67 //TODO Very simple heuristic for dynamic GC buffer resizing:
68 //If there are "too few" collections, increase the collection threshold
69 //by a fixed step
70 //Adapted from zend_gc.c/gc_adjust_threshold() as of PHP 8.3.14
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);
75 }
76 }
77
78 public function getThreshold() : int{ return $this->threshold; }
79
80 public function getCollectionTimeTotalNs() : int{ return $this->collectionTimeTotalNs; }
81
82 public function maybeCollectCycles() : int{
83 $rootsBefore = gc_status()["roots"];
84 if($rootsBefore < $this->threshold){
85 return 0;
86 }
87
88 $this->timings->startTiming();
89
90 $start = hrtime(true);
91 $cycles = gc_collect_cycles();
92 $end = hrtime(true);
93
94 $rootsAfter = gc_status()["roots"];
95 $this->adjustGcThreshold($cycles, $rootsAfter);
96
97 $this->timings->stopTiming();
98
99 $time = $end - $start;
100 $this->collectionTimeTotalNs += $time;
101 $this->runs++;
102 $this->logger->info(sprintf(
103 "Run #%d took %s ms (%s -> %s roots, %s cycles collected) - cumulative GC time: %s ms",
104 $this->runs,
105 number_format($time / 1_000_000, 2),
106 $rootsBefore,
107 $rootsAfter,
108 $cycles,
109 number_format($this->collectionTimeTotalNs / 1_000_000, 2)
110 ));
111
112 return $cycles;
113 }
114}