wuliaonimei博客
随机文章
随机文章

记一个yii2下面的内存泄漏问题

时间:2017年07月19日  分类:php  标签:yii2,php,gc  评论:0

1 发现问题

public static function actionTest() {

        $total = 10;

        var_dump('开始内存'.memory_get_usage());

        while($total){

            $ret=User::findOne(['id'=>910002]);

            var_dump('end内存'.memory_get_usage());

            unset($ret);

            $total--;

        }

    }

上面代码的内存一直在增长, 按照原本想法来看, 变量被释放了,内存就算增长也不会一直增长。因为每循环一次内存都会被释放。

2 分析问题

上面这段代码涉及到了数据库的操作,而我们知道,数据库的很多地方都能引起内存泄漏。 所以先屏蔽数据库相关操作,

我手写了一个原生的数据库查询操作, 发现内存正常,没有问题。

$dsn "mysql:dbname=test;host=localhost";

$db_user 'root';

$db_pass 'admin';

//查询

$sql "select * from buyer";

$res $pdo->query($sql);

foreach($res as $row) {

    echo $row['username'].'<br/>';

}

这时候答案呼之欲出---  是yii2框架搞了鬼

3 定位问题

既然知道了是yii2 框架的问题那就可以进一步缩小问题。

public static function actionTest() {

        $total = 10;

        var_dump('开始内存'.memory_get_usage());

        while($total){

            $ret= new User();

            var_dump('end内存'.memory_get_usage());

            unset($ret);

            $total--;

        }

    }

内存还是一直增长。 这时候我测试了一个其他的yii2类 发觉内存不增长了。

这就可以联想到是在new 对象的时候yii2内部自己执行了什么操作,然后导致内存泄漏。

什么方法是new 的时候就执行的呢。。。

对的 构造方法 __construct  。

然后 我一步一步的从model 查到object 发觉都没有能引起泄漏的地方。

这个时候我们不妨换个思路, 既然是yii2框架下出现的泄漏, 那肯定就是yii2独有的功能,

那什么功能是yii2独有的,又是在new 对象的时候就会执行的呢?

3.1 行为(Behavior)

发觉我的模型类里面果然有用了行为

public function behaviors()

    {

        return [

            TimestampBehavior::class,

        ];

    }

最普通不过的代码。

我们知道 行为最后调用的地方是

yii\base\Component->attachBehaviors

最后定位到

private function attachBehaviorInternal($name$behavior)

    {

        if (!($behavior instanceof Behavior)) {

            $behavior = Yii::createObject($behavior);

        }

        if (is_int($name)) {

            $behavior->attach($this);

            $this->_behaviors[] = $behavior;

        else {

            if (isset($this->_behaviors[$name])) {

                $this->_behaviors[$name]->detach();

            }

            $behavior->attach($this);

            $this->_behaviors[$name] = $behavior;

        }

 

        return $behavior;

    }

我们观察这段代码,发觉他吧自己传进去了$behavior->attach($this);

最后调用的是 yii\base\Behavior->attach

public function attach($owner)

    {

        $this->owner = $owner;

        foreach ($this->events() as $event => $handler) {

            $owner->on($eventis_string($handler) ? [$this$handler] : $handler);

        }

    }

这个时候答案已经呼之欲出, yii2为了实现行为这一功能, 把自身this传进去,以便能注册事件,触发事件。解除事件。

这就导致了一个循环引用的问题。 所以导致对象refcount一直不为0 一直回收不了。

关于php垃圾回收机制,yii2的行为相关知识等还是自己查资料吧



评论列表

回复

你正在以游客身份访问网站,请输入你的昵称和 E-mail

Copyright ©2014-2015 Develop by Skilly. Go to the Top