2

我在我的项目中使用 NestedSetBehavior 模型扩展来将 db 表用作树。

我写了一个例子:

    $model = SiteMap()->findAll();
    $log .= $this->debug_memory('used')."<br>";
    $ancestors = null;
    foreach ($model as $item) {
        $ancestors = $item->ancestors()->findAll();
    }
    $log .= $this->debug_memory('used')."<br>";
    echo $log;

(debug_memory 源只返回友好的 memory_get_usage(),$model 有 50 个项目)

结果是:

used: 10525440
used: 15892712

经过简单的计算 - 内存使用量增加了 5,24 Mb。

但我必须使用 $item->ancestors()->findAll(); 循环多次,所以我的内存增加了 138 Mb。我得到@out of memory 错误”。

我尝试使用 unset():

    $model = SiteMap()->findAll();
    $log .= $this->debug_memory('used')."<br>";
    $ancestors = null;
    foreach ($model as $item) {
       $ancestors= $item->ancestors()->findAll();
    }
    $ancestors = null;
    unset($ancestors);
    $log .= $this->debug_memory('used')."<br>";
    echo $log;

但我得到了结果:

used: 10525984
used: 15893320

行为祖先函数源码为:

public function ancestors($depth=null)
{
    $owner=$this->getOwner();
    $db=$owner->getDbConnection();
    $criteria=$owner->getDbCriteria();
    $alias=$db->quoteColumnName($owner->getTableAlias());

    $criteria->mergeWith(array(
        'condition'=>$alias.'.'.$db->quoteColumnName($this->leftAttribute).'<'.$owner->{$this->leftAttribute}.
            ' AND '.$alias.'.'.$db->quoteColumnName($this->rightAttribute).'>'.$owner->{$this->rightAttribute},
        'order'=>$alias.'.'.$db->quoteColumnName($this->leftAttribute),
    ));

    if($depth!==null)
        $criteria->addCondition($alias.'.'.$db->quoteColumnName($this->levelAttribute).'>='.($owner->{$this->levelAttribute}-$depth));

    if($this->hasManyRoots)
    {
        $criteria->addCondition($alias.'.'.$db->quoteColumnName($this->rootAttribute).'='.CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount);
        $criteria->params[CDbCriteria::PARAM_PREFIX.CDbCriteria::$paramCount++]=$owner->{$this->rootAttribute};
    }

    return $owner;
}

所以,我的问题是,为什么这个函数使用这么多内存,为什么当我取消设置变量内存时没有清理?

4

2 回答 2

4

注释掉protected/config/main.php文件中的日志记录。(或者您定义配置设置的任何位置。)

您所看到的可能是所有日志都写入每个 Active Record 调用的结果,这可以解释为什么取消设置对象不会释放内存:使用的内存不在模型中,而是在日志中。

尝试并报告结果。

于 2012-12-01T18:08:47.657 回答
1

回答

禁用日志(正如@willem-renzema 已经说过的那样)以防止它使用内存(尤其是在测试泄漏时)总是好的。

但是,您应该detachBehaviors()在示例代码中调用每个祖先来阻止它泄漏内存。在使用之前对每个带有附加行为的对象实例执行此操作unset以防止发生内存泄漏是一种很好的行为。即使您没有明确地“取消设置”对象(例如,您的变量正在超出范围)。

有关NestedSetBehavior 中此问题的更多信息,请参阅https://github.com/yiiext/nested-set-behavior/issues/25 。

另请参阅https://github.com/yiisoft/yii/issues/1329#issuecomment-18729026,了解有关 PHP 和 Yii 中的内存泄漏和循环引用的更一般性讨论。


笔记

您可能很想使用析构函数 ( __destruct()) 为您处理行为分离。但在这种特定情况下,由于这里提到的析构函数规则,它不起作用

“对象只会 在所有__destruct()引用都未设置时释放它们的资源并触发它们的方法。即使它们对象中......叹息!” -(俄勒冈州的氮氧化物,2009 年)。

仅在“祖先”上使用不会满足此规则,unset因为 NestedSetBehavior 仍然具有对存储在名为的静态数组中的“祖先”实例的引用:$_cached。只有通过销毁 NestedSetBehavior 实例本身才能清除此引用,这将在您调用detachBehaviors()行为的“所有者”对象时发生。

于 2014-06-10T14:08:25.093 回答