减少PHP中递归函数的内存使用量

时间:2017-09-26 14:29:39

标签: php recursion

我在PHP中有一个递归函数,它在一个API中循环,它允许你一次恢复200条记录。

但由于此API具有非常高的响应延迟,因此我们决定使用本地中间数据库添加这些记录,并在网站上显示相同的记录。

然而,由于这个API有超过30000条记录的递归函数,它消耗了大量内存,因为在30000条记录的情况下,它必须以递归方式调用超过1500次,最终得到着名的StackOverflow

我想知道是否有一种手动方式可以通过再次调用此函数来清除此函数的内存而不会丢失它的值。

代码示例:

public function recursive ($index = 0, $offset = 200) {
   $api = GetConectApi ($index, offset)-> Getrecords ();
   foreach ($api $value) {
      \\Here is my necessary loop
   }
   if (count ($API) > 0) {
      $this Recursive ($index + 200, $offset + 200);
   }
}

我想找到一种方法,当它调用递归函数时,再次消除了先前的分配而不会丢失传递的参考值。

3 个答案:

答案 0 :(得分:2)

完成后,您可以尝试对$ api变量进行清理。

$cnt = count($api);
$api = null;
unset($api);
if ( $cnt > 0) {

答案 1 :(得分:2)

要扩展user3720435's answer,每次运行该函数时,都会通过创建新的$api变量来占用大量内存。为了理解原因,让我们“展开”代码 - 想象一下,所有代码都按顺序写出,没有函数调用:

$api1 = GetConectApi ($index1, offset1)-> Getrecords ();
foreach ($api1 => $value1) {
    // Here is my necessary loop
}
if (count ($api1) > 0) {
    // RECURSION HAPPENS HERE
    $index2 = $index1 + 200, $offset2 = $offset1 + 200
    $api2 = GetConectApi ($index, offset)-> Getrecords ();
    foreach ($api2 => $value2) {
        // Here is my necessary loop
    }
    if (count ($api2) > 0) {
        // RECURSE AGAIN, AND AGAIN, AND AGAIN
    }
}

请注意,我已将所有变量重命名为$api1$api2等。这是因为每次运行该函数时,$api实际上都是一个不同的变量。它在源代码中具有相同的名称,但它不代表同一块内存。

现在,PHP不知道你在创建$api1时不会再次使用$api2,所以它必须将两者都保留在内存中;当你最终获得越来越多的数据集时,它需要越来越多的内存。

user3720435的建议是在递归之前添加unset($api)

$api = GetConectApi ($index, offset)-> Getrecords ();
foreach ($api => $value) {
      // Here is my necessary loop
}
if (count ($api) > 0) {
      unset($api);
      // code as before
}

这告诉PHP你不再需要那个内存,所以当它递归时,它不会累积。您仍会构建$index$offset的多个副本,但相比之下这些副本可能非常小。

所有这一切,目前尚不清楚为什么你需要递归。整个事情实际上可以改成一个简单的循环:

do {
    $api = GetConectApi ($index, offset)-> Getrecords ();
    foreach ($api => $value1) {
       // Here is my necessary loop
    }
    $index = $index + $offset;
} while (count ($api) > 0)

do..while循环总是执行一次,然后不断重复直到条件变为false。展开它看起来像这样:

// do...
    $api = GetConectApi ($index, offset)-> Getrecords ();
    foreach ($api => $value1) {
       // Here is my necessary loop
    }
    $index = $index + $offset;
if (count ($api) > 0) { // while...
$api = GetConectApi ($index, offset)-> Getrecords ();
    foreach ($api => $value1) {
       // Here is my necessary loop
    }
    $index = $index + $offset;
}
if (count ($api) > 0) { // while...
// etc

请注意,我们不需要在循环中分配任何额外的内存,因为我们还没有输入新函数 - 我们只是一遍又一遍地使用相同的变量。

答案 2 :(得分:1)

您可以使用队列系统获取所有数据并将其保存到数据库,如RMQ

或者您可以在数据库中设置[index],设置为0

然后你添加cron作业来从API获取数据而不递归,它会每分钟运行一次,例如

它将转到db get index并获得偏移并获取数据并增加索引

1分钟后,作业将再次运行转到db get index并获得偏移并获取数据并增加索引等等