长时间运行的PHP脚本的内存注意事项

Jur*_*man 15 php memory garbage-collection command-line-interface beanstalkd

我想使用Zend Framework 2控制器在php中为beanstalkd编写一个worker .它通过CLI启动并将永久运行,从此示例中请求来自beanstalkd的作业.

在简单的伪类代码中:

while (true) {
    $data   = $beanstalk->reserve();

    $class  = $data->class;
    $params = $data->params;

    $job    = new $class($params);
    $job();
}
Run Code Online (Sandbox Code Playgroud)

$job这里有一个__invoke()过程的方法.但是,这些工作中的某些事情可能会持续很长时间.有些可能会运行相当大的内存.有些人可能注入了$beanstalk对象,自己开始新的工作,或者有一个Zend\Di\Locator实例从DIC中提取对象.

我很担心这种设置对于在长期的生产环境,为可能的循环引用可能会产生,(此时)我不明确"做"任何垃圾回收,而这个动作可能运行数周/月/年*.

*)在beanstalk中,reserve是一个阻塞调用,如果没有可用的作业,这个worker将等到从beanstalk获得任何响应.

我的问题:php如何长期处理这个问题,我是否应该采取任何特殊的预防措施来防止这种情况发生?

我确实考虑过并且可能会有所帮助(但如果我错了请更正并在可能的情况下添加更多内容):

  1. 在开始循环之前使用gc_enable()
  2. 在每次迭代中使用gc_collect_cycles()
  3. $job在每次迭代中取消设置
  4. __destruct()从a中明确取消引用$job

(注意:从这里更新)

我确实用任意工作进行了一些测试.我包括的工作是:"简单",只是设置一个值; "longarray",创建一个包含1,000个值的数组; "producer",让循环注入$pheanstalk并向队列添加三个simplejobs(所以现在有一个从job到beanstalk的引用); "locatoraware",其中Zend\Di\Locator给出了a 并且实例化了所有作业类型(尽管未调用).我在队列中添加了10,000个作业,然后我将所有作业保留在队列中.

"simplejob"的结果(每1000个工作的内存消耗,有memory_get_usage())

0:     56392
1000:  548832
2000:  1074464
3000:  1538656
4000:  2125728
5000:  2598112
6000:  3054112
7000:  3510112
8000:  4228256
9000:  4717024
10000: 5173024
Run Code Online (Sandbox Code Playgroud)

选择随机工作,测量与上述相同.分配:

["Producer"] => int(2431)
["LongArray"] => int(2588)
["LocatorAware"] => int(2526)
["Simple"] => int(2456)
Run Code Online (Sandbox Code Playgroud)

记忆:

0:     66164
1000:  810056
2000:  1569452
3000:  2258036
4000:  3083032
5000:  3791256
6000:  4480028
7000:  5163884
8000:  6107812
9000:  6824320
10000: 7518020
Run Code Online (Sandbox Code Playgroud)

上面的执行代码更新为:

$baseMemory = memory_get_usage();
gc_enable();

for ( $i = 0; $i <= 10000; $i++ ) {
    $data = $bheanstalk->reserve();

    $class = $data->class;
    $params = $data->params;

    $job = new $class($params);
    $job();

    $job = null;
    unset($job);

    if ( $i % 1000 === 0 ) {
        gc_collect_cycles();
        echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "<br>";
    }
}
Run Code Online (Sandbox Code Playgroud)

正如大家所注意到的那样,内存消耗在php中并未被利用并保持在最低限度,但随着时间的推移而增加.

Jur*_*man 2

我最终对当前的代码基准线进行了基准测试,之后我得出了以下结论:

$job = $this->getLocator()->get($data->name, $params);
Run Code Online (Sandbox Code Playgroud)

它使用Zend\Di依赖注入,实例管理器在整个过程中跟踪实例。因此,在调用作业并可以将其删除后,实例管理器仍将其保留在内存中。不立即用于Zend\Di实例化作业会导致静态内存消耗,而不是线性内存消耗。