迭代Drupal 8 EntityQuery结果时达到PHP内存限制。我怎样才能控制住它?

迭代Drupal 8 EntityQuery结果时达到PHP内存限制。我怎样才能控制住它?,php,memory,memory-management,drupal-8,Php,Memory,Memory Management,Drupal 8,我有一个D8API端点,它查询特定的内容类型,应用任何可选条件,将结果转换为JSON,并返回给客户端。我将PHP内存限制更新为512M,但仍在运行中。Drupal中只有1500条记录,所以真的不应该有任何理由说明它如此糟糕(每条记录341KB?!)。如果我只是不断增加内存以使其运行,则呈现的JSON小于2MB 我知道PHP垃圾收集是自动的,所以我猜有一些引用被保留了下来 我做了几次尝试来抑制它,比如批处理查询、重构成函数,以及显式调用gc\u collect\u cycles,但没有任何区别 在

我有一个D8API端点,它查询特定的内容类型,应用任何可选条件,将结果转换为JSON,并返回给客户端。我将PHP内存限制更新为512M,但仍在运行中。Drupal中只有1500条记录,所以真的不应该有任何理由说明它如此糟糕(每条记录341KB?!)。如果我只是不断增加内存以使其运行,则呈现的JSON小于2MB

我知道PHP垃圾收集是自动的,所以我猜有一些引用被保留了下来

我做了几次尝试来抑制它,比如批处理查询、重构成函数,以及显式调用
gc\u collect\u cycles
,但没有任何区别

在迭代Drupal EntityQuery的结果时,如何降低内存消耗

  protected function get() {
    echo "memory (start): " . memory_get_usage() . "\n<br>";

    //some setup and validation

    $query = $this->build_query($params);
    echo "memory (build_query): " . memory_get_usage() . "\n<br>";

    $results = $query->execute();
    echo "memory (execute): " . memory_get_usage() . "\n<br>";

    $items = [];

    $chunk_size = 50;
    $chunks = array_chunk(array_values($results), $chunk_size);
    echo "memory (chunk): " . memory_get_usage() . "\n<br>";

    foreach ($chunks as $chunk) {
      $items = array_merge($items, $this->load_nodes($chunk));
      echo "memory (chunk loaded): " . memory_get_usage() . "\n<br>";
    }
    echo "memory (all loaded): " . memory_get_usage() . "\n<br>";

    $response = [ 'results' => $items ];
    return new ResourceResponse($response);
  }

与存储器回波相关的输出为:

内存(开始):28297032
内存(构建查询):29984168
内存(执行):31004048
内存(块):31083864
内存(块加载):42175976
内存(已加载块):50447792
内存(块加载):57609344
内存(块加载):66762688
内存(块加载):74555712
内存(块加载):86663016
内存(块加载):98514192
内存(块加载):110908336
内存(块加载):122792592
内存(块加载):134651328
内存(块加载):145622512
内存(块加载):156546072
内存(块加载):167805352
内存(块加载):178617040
内存(块加载):190400936
内存(块加载):201246256
内存(已加载块):212387384
内存(已加载块):223756088
内存(已加载块):234898632
内存(块加载):246125624
内存(块加载):257136304
内存(块加载):268205304
内存(块加载):278744896
内存(已加载块):289693184
内存(已加载块):300491840
内存(块加载):310564624
内存(已加载块):321204064
内存(块加载):333842760
内存(块加载):343723672
内存(块加载):344960728
内存(全部加载):344960728

当GC清理旧引用时,每次迭代
load\u节点时,内存消耗不应该保持稳定吗


您会注意到,我的端点仅以344 MB结束。这个错误实际上是在Drupal核心的某个地方抛出的。因为我想将最大PHP内存保持在128M,所以我仍然需要降低我的部分内存。

事实上,我认为你关于垃圾收集的假设在这种特殊情况下是不正确的

来自Drupal 8文档:

功能节点\u加载\u多个

从数据库加载节点实体

当需要加载多个文件时,应使用此函数 从数据库中删除节点节点已加载到内存中,并且不会 如果在同一页面请求期间再次加载,则需要访问数据库。

它们似乎打算在页面请求的整个持续时间内保持不变,这将使内存消耗累积,即使在迭代中也是如此

事实上,我在Drupal论坛上看到很多其他开发人员在使用此功能时也遇到内存不足的问题。如果加载多个节点,内存消耗将特别高


为了降低内存消耗,我建议通过将cache reset参数设置为true来禁用节点负载的缓存。例如:

$nodes = node_load_multiple($ids, NULL, TRUE);
希望有帮助:)


编辑:

Hrmm看起来我们在正确的轨道上尝试重置缓存,但是我们必须尝试另一种方法来重置它。此方法来自不推荐使用的
节点加载()
函数

Drupal中备用重置缓存方法的类路径如下:

\Drupal::entityManager()->getStorage('node')->resetCache(array('NID'));
固定脚本类似于:

$query = \Drupal::entityQuery('node')
     ->condition($params);

$results = $query->execute();

$nids = array_keys($results);

foreach ($nids as $nid) {
    $node = \Drupal\node\Entity\Node::load($nid);

    // Do stuff with loaded node, ex:
    // print $node->title->value;

    // Now reset the cache with the legacy reset cache
    \Drupal::entityManager()->getStorage('node')->resetCache(array($nid));
}

我认为您对PHP中的垃圾收集器存在误解

垃圾收集器释放内存空间的唯一方法是当这个空间不再被任何变量引用时,同时,您总是从函数返回值,以便它们被高级函数中的其他变量引用


您还可以检查如何禁用drupal中的某些缓存,这可能会对您有所帮助,具体取决于drupal使用的缓存策略

返回的项是简单数组,而不是drupal查询返回的项。它们当然不应该在每条记录中占用大约340KB的空间。我没有保存对查询返回的Drupal对象的引用。
垃圾收集器释放内存空间的唯一方法是当任何变量不再引用该空间时。
-规则有很多例外,有很多函数即使在引用保留时也会释放内存,包括fclose()、curl\u close(),curl_multi_close()、socket_close()和imagedestroy(),这些都是我脑子里想不到的,我可能忘了一些东西你提到的那些函数不是释放内存空间而是释放资源,你可以告诉我这些资源消耗内存,因此当我们关闭它们时内存被释放,我可以回答,如果你看看我在回答中说的话:-->phpy中的垃圾收集器你在末尾尝试这段代码$handle不等于false,但仍然引用了一个resourceAw太糟糕了,测试发生了什么,只是仍然达到了内存限制?是的。仍然达到记忆极限。对我有效的答案就在这条线上。把它加到你的答案里,我会给你赏金的。我不能让所有这些好的互联网点白白浪费掉。你真是太好了@Jeff,很高兴听到你能在船尾工作
\Drupal::entityManager()->getStorage('node')->resetCache(array('NID'));
$query = \Drupal::entityQuery('node')
     ->condition($params);

$results = $query->execute();

$nids = array_keys($results);

foreach ($nids as $nid) {
    $node = \Drupal\node\Entity\Node::load($nid);

    // Do stuff with loaded node, ex:
    // print $node->title->value;

    // Now reset the cache with the legacy reset cache
    \Drupal::entityManager()->getStorage('node')->resetCache(array($nid));
}