zanphp
zanphp copied to clipboard
协程的底层有一处写的有问题,会出现内存溢出
文件 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_KILLED
和 Signal::TASK_DONE
时调用。这样在协程处理完毕后,会立即释放对象,避免内存溢出。
已知的问题, 循环引用会影响垃圾回收, 导致 gcroot 的 scan 过程的耗时增加