The Purpose and Utility of the gc_collect_cycles Function

When you continuously use a significant amount of memory for a short duration and then switch to using much less memory for an extended period, explicitly collecting memory can make a substantial difference. Specifically, I am experiencing memory consumption issues with the queue workers in my Laravel based application running on php 7.0.


Question:

Could someone clarify the scenarios where the
Gc_collect_cycles
function is beneficial? Is it necessary to invoke it prior to significant memory usage occurring?


Solution:


By default, PHP has the “Garbage Collector” feature enabled, which is responsible for freeing memory occupied by “garbage”. The function

gc_collect_cycles()

can be used to manually trigger the collection of any existing garbage cycles and returns the number of collected cycles (such as objects and variable values). The enabled Garbage Collector internally calls this function periodically to free up resources. Typically, PHP scripts have a short lifespan, so all garbage is automatically destroyed at the end of execution without the need for garbage collection.

Occasionally, there is a requirement to manually handle GC.

  1. Using

    gc_disable()

    can enhance the speed of certain lengthy processes, albeit at the expense of increased memory usage.
  2. The appropriate instances of GC could be indicated using

    gc_collect_cycles()

    .

There is an additional motive to utilize

gc_collect_cycles()

– debugging. Suppose you wish to determine the memory usage of a specific code block using

memory_get_usage()

. In order to obtain accurate results, it is necessary to disable the GC. Afterwards, you can distinguish the time and memory utilized by the GC and your application by invoking

gc_collect_cycles()

and measuring the timings/memory before and after.

A small illustration:

class A {
  public $ref;
  public $name;
  public function __construct($name) {
    $this->name = $name;
    echo($this->name.'->__construct();'.PHP_EOL);
  }
  public function __destruct() {
    echo($this->name.'->__destruct();'.PHP_EOL);
  }
}
gc_disable();
$a1 = new A('$a1');
$a2 = new A('$a2');
$a1->ref = $a2;
$a2->ref = $a1;
$b = new A('$b');
$b->ref = $a1;
echo('$a1 = $a2 = $b = NULL;'.PHP_EOL);
$a1 = $a2 = $b = NULL;
echo('gc_collect_cycles();'.PHP_EOL);
echo('// removed cycles: '.gc_collect_cycles().PHP_EOL);
echo('exit();'.PHP_EOL);

Will output:

$a1->__construct();
$a2->__construct();
$b->__construct();
$a1 = $a2 => $b = NULL;
$b->__destruct();
gc_collect_cycles();
$a2->__destruct();
$a1->__destruct();
// removed cycles: 4

This indicates that the destruction request only affected

$b

. However, the other objects (

$a1

and

$a2

) have cyclic references and their properties are also
consume memory
. As a result, four entities (two objects and two strings) were removed by

gc_collect_cycles()

.

Frequently Asked Questions

Posted in Php