GenisysPro  for Minecraft PE/Windows 10 v1.1.x
Feature-rich server software for Minecraft PE and Windows 10 Edition
MemoryManager Class Reference

Public Member Functions

 __construct (Server $server)
 
 isLowMemory ()
 
 canUseChunkCache ()
 
 getViewDistance (int $distance)
 
 trigger ($memory, $limit, $global=false, $triggerCount=0)
 
 check ()
 
 triggerGarbageCollector ()
 
 addObjectWatcher ($object)
 
 isObjectAlive ($id)
 
 removeObjectWatch ($id)
 
 doObjectCleanup ()
 
 getObjectInformation ($id, $includeObject=false)
 
 dumpServerMemory ($outputFolder, $maxNesting, $maxStringSize)
 

Constructor & Destructor Documentation

◆ __construct()

__construct ( Server  $server)

MemoryManager constructor.

Parameters
Server$server
79  {
80  $this->server = $server;
81 
82  $this->init();
83  }

Member Function Documentation

◆ addObjectWatcher()

addObjectWatcher (   $object)
Parameters
object$object
Returns
string Object identifier for future checks
262  {
263  if(!is_object($object)){
264  throw new \InvalidArgumentException("Not an object!");
265  }
266 
267 
268  $identifier = spl_object_hash($object) . ":" . get_class($object);
269 
270  if(isset($this->leakInfo[$identifier])){
271  return $this->leakInfo["id"];
272  }
273 
274  $this->leakInfo[$identifier] = [
275  "id" => $id = md5($identifier . ":" . $this->leakSeed++),
276  "class" => get_class($object),
277  "hash" => $identifier
278  ];
279  $this->leakInfo[$id] = $this->leakInfo[$identifier];
280 
281  $this->leakWatch[$id] = new \WeakRef($object);
282 
283  return $id;
284  }

◆ canUseChunkCache()

canUseChunkCache ( )
Returns
bool
149  {
150  return !($this->lowMemory and $this->chunkTrigger);
151  }

◆ check()

check ( )
196  {
197  Timings::$memoryManagerTimer->startTiming();
198 
199  if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){
200  $this->checkTicker = 0;
201  $memory = Utils::getMemoryUsage(true);
202  $trigger = false;
203  if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){
204  $trigger = 0;
205  }elseif($this->globalMemoryLimit > 0 and $memory[1] > $this->globalMemoryLimit){
206  $trigger = 1;
207  }
208 
209  if($trigger !== false){
210  if($this->lowMemory and $this->continuousTrigger){
211  if(++$this->continuousTriggerTicker >= $this->continuousTriggerRate){
212  $this->continuousTriggerTicker = 0;
213  $this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0, ++$this->continuousTriggerCount);
214  }
215  }else{
216  $this->lowMemory = true;
217  $this->continuousTriggerCount = 0;
218  $this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0);
219  }
220  }else{
221  $this->lowMemory = false;
222  }
223  }
224 
225  if($this->garbageCollectionPeriod > 0 and ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){
226  $this->garbageCollectionTicker = 0;
227  $this->triggerGarbageCollector();
228  }
229 
230  Timings::$memoryManagerTimer->stopTiming();
231  }

◆ doObjectCleanup()

doObjectCleanup ( )
311  {
312  foreach($this->leakWatch as $id => $w){
313  if(!$w->valid()){
314  $this->removeObjectWatch($id);
315  }
316  }
317  }

◆ dumpServerMemory()

dumpServerMemory (   $outputFolder,
  $maxNesting,
  $maxStringSize 
)
Parameters
$outputFolder
$maxNesting
$maxStringSize
357  {
358  gc_disable();
359  ini_set("memory_limit", -1);
360  if(!file_exists($outputFolder)){
361  mkdir($outputFolder, 0777, true);
362  }
363  $this->server->getLogger()->notice("[Dump] After the memory dump is done, the server will shut down");
364 
365  $obData = fopen($outputFolder . "/objects.js", "wb+");
366 
367  $staticProperties = [];
368 
369  $data = [];
370 
371  $objects = [];
372 
373  $refCounts = [];
374 
375  $this->continueDump($this->server, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
376 
377  do{
378  $continue = false;
379  foreach($objects as $hash => $object){
380  if(!is_object($object)){
381  continue;
382  }
383  $continue = true;
384 
385  $className = get_class($object);
386 
387  $objects[$hash] = true;
388 
389  $reflection = new \ReflectionObject($object);
390 
391  $info = [
392  "information" => "$hash@$className",
393  "properties" => []
394  ];
395 
396  if($reflection->getParentClass()){
397  $info["parent"] = $reflection->getParentClass()->getName();
398  }
399 
400  if(count($reflection->getInterfaceNames()) > 0){
401  $info["implements"] = implode(", ", $reflection->getInterfaceNames());
402  }
403 
404  foreach($reflection->getProperties() as $property){
405  if($property->isStatic()){
406  continue;
407  }
408 
409  if(!$property->isPublic()){
410  $property->setAccessible(true);
411  }
412  $this->continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
413  }
414 
415  fwrite($obData, "$hash@$className: " . json_encode($info, JSON_UNESCAPED_SLASHES) . "\n");
416 
417  if(!isset($objects["staticProperties"][$className])){
418  $staticProperties[$className] = [];
419  foreach($reflection->getProperties() as $property){
420  if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){
421  continue;
422  }
423 
424  if(!$property->isPublic()){
425  $property->setAccessible(true);
426  }
427  $this->continueDump($property->getValue($object), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize);
428  }
429  }
430  }
431 
432  echo "[Dump] Wrote " . count($objects) . " objects\n";
433  }while($continue);
434 
435  fclose($obData);
436 
437  file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
438  file_put_contents($outputFolder . "/serverEntry.js", json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
439  file_put_contents($outputFolder . "/referenceCounts.js", json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
440 
441  echo "[Dump] Finished!\n";
442 
443  gc_enable();
444  }

◆ getObjectInformation()

getObjectInformation (   $id,
  $includeObject = false 
)
Parameters
$id
bool$includeObject
Returns
array|null
325  {
326  if(!isset($this->leakWatch[$id])){
327  return null;
328  }
329 
330  $valid = false;
331  $references = 0;
332  $object = null;
333 
334  if($this->leakWatch[$id]->acquire()){
335  $object = $this->leakWatch[$id]->get();
336  $this->leakWatch[$id]->release();
337 
338  $valid = true;
339  $references = getReferenceCount($object, false);
340  }
341 
342  return [
343  "id" => $id,
344  "class" => $this->leakInfo[$id]["class"],
345  "hash" => $this->leakInfo[$id]["hash"],
346  "valid" => $valid,
347  "references" => $references,
348  "object" => $includeObject ? $object : null
349  ];
350  }

◆ getViewDistance()

getViewDistance ( int  $distance)

Returns the allowed chunk radius based on the current memory usage.

Parameters
int$distance
Returns
int
160  : int{
161  return $this->lowMemory ? min($this->chunkRadiusOverride, $distance) : $distance;
162  }

◆ isLowMemory()

isLowMemory ( )
Returns
bool
142  {
143  return $this->lowMemory;
144  }

◆ isObjectAlive()

isObjectAlive (   $id)
Parameters
$id
Returns
bool
291  {
292  if(isset($this->leakWatch[$id])){
293  return $this->leakWatch[$id]->valid();
294  }
295 
296  return false;
297  }

◆ removeObjectWatch()

removeObjectWatch (   $id)
Parameters
$id
302  {
303  if(!isset($this->leakWatch[$id])){
304  return;
305  }
306  unset($this->leakInfo[$this->leakInfo[$id]["hash"]]);
307  unset($this->leakInfo[$id]);
308  unset($this->leakWatch[$id]);
309  }

◆ trigger()

trigger (   $memory,
  $limit,
  $global = false,
  $triggerCount = 0 
)
Parameters
$memory
$limit
bool$global
int$triggerCount
170  {
171  $this->server->getLogger()->debug("[Memory Manager] " . ($global ? "Global " : "") . "Low memory triggered, limit " . round(($limit / 1024) / 1024, 2) . "MB, using " . round(($memory / 1024) / 1024, 2) . "MB");
172 
173  if($this->cacheTrigger){
174  foreach($this->server->getLevels() as $level){
175  $level->clearCache(true);
176  }
177  }
178 
179  if($this->chunkTrigger and $this->chunkCollect){
180  foreach($this->server->getLevels() as $level){
181  $level->doChunkGarbageCollection();
182  }
183  }
184 
185  $ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount);
186  $this->server->getPluginManager()->callEvent($ev);
187 
188  $cycles = 0;
189  if($this->garbageCollectionTrigger){
190  $cycles = $this->triggerGarbageCollector();
191  }
192 
193  $this->server->getLogger()->debug("[Memory Manager] Freed " . round(($ev->getMemoryFreed() / 1024) / 1024, 2) . "MB, $cycles cycles");
194  }

◆ triggerGarbageCollector()

triggerGarbageCollector ( )
Returns
int
236  {
237  Timings::$garbageCollectorTimer->startTiming();
238 
239  if($this->garbageCollectionAsync){
240  $size = $this->server->getScheduler()->getAsyncTaskPoolSize();
241  for($i = 0; $i < $size; ++$i){
242  $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GarbageCollectionTask(), $i);
243  }
244  }
245 
246  $cycles = gc_collect_cycles();
247 
248  foreach($this->server->getLevels() as $level){
249  $level->doChunkGarbageCollection();
250  }
251 
252  Timings::$garbageCollectorTimer->stopTiming();
253 
254  return $cycles;
255  }

The documentation for this class was generated from the following file: