zanphp icon indicating copy to clipboard operation
zanphp copied to clipboard

协程的底层有一处写的有问题,会出现内存溢出

Open breath-co2 opened this issue 7 years ago • 1 comments

文件 https://github.com/zanphp/coroutine/blob/master/src/Task.php 第50行如下:

 public function __construct(\Generator $coroutine, Context $context = null, $taskId = 0, Task $parentTask = null)
    {
        $this->coroutine = $this->caughtCoroutine($coroutine);
        $this->taskId = $taskId ? $taskId : TaskId::create();
        $this->parentTask = $parentTask;
        if ($context) {
            $this->context = $context;
        } else {
            $this->context = new Context();
        }
        $this->scheduler = new Scheduler($this);
    }

这个 $this->scheduler = new Scheduler($this); 这个会把 Scheduler 放在自己的寄存器里,然而 Scheduler 这个对象又把 Task 放在了自己的 task 变量里。形成了相互引用。在处理完毕后并不会释放对象,只有在php垃圾回收机制下才会被清理。不信的话,可以加

    public function __destruct()
    {
        echo "i'm unset";
    }

会发现并未协程执行完毕后这个方法并未被执行,内存中还存在。

建议Task中加:

    public function release()
    {
        # 移除对象,避免相互引用导致的对象不可释放
        if (isset($this->scheduler))
        {
            $this->scheduler->release();
            unset($this->scheduler);
        }
    }

Scheduler 中加:

    public function release()
    {
        unset($this->task);
    }

并在 Signal::TASK_KILLEDSignal::TASK_DONE 时调用。这样在协程处理完毕后,会立即释放对象,避免内存溢出。

breath-co2 avatar Nov 15 '17 07:11 breath-co2

已知的问题, 循环引用会影响垃圾回收, 导致 gcroot 的 scan 过程的耗时增加

goghcrow avatar Nov 16 '17 03:11 goghcrow