php please release resources manually - yaokun123/php-wiki GitHub Wiki

请手动释放你的资源

"PHP在请求结束后会释放所有的资源, 所以我们没有必要调用fclose,或者mysql_close来释放资源, PHP会替我们做"

在PHP5.2以前, PHP使用引用计数(Reference count)来做资源管理, 当一个zval的引用计数为0的时候, 它就会被释放. 虽然存在循环引用(Cycle reference), 但这样的设计对于开发Web脚本来说, 没什么问题, 因为Web脚本的特点和它追求的目标就是执行时间短, 不会长期运行. 对于循环引用造成的资源泄露, 会在请求结束时释放掉. 也就是说, 请求结束时释放资源, 是一种部补救措施(backup)

然而, 随着PHP被越来越多的人使用, 就有很多人在一些后台脚本使用PHP, 这些脚本的特点是长期运行, 如果存在循环引用, 导致引用计数无法及时释放不用的资源, 则这个脚本最终会内存耗尽退出。

所以在PHP5.3以后, 引入了GC, 也就是说, 我们引入GC是为了解决用户无法解决的问题。

现在让我们回头来看开头的问题, 是不是因为PHP会在请求结束后释放所有的资源, 于是我们就可以不用手动释放呢?

Mysql最大连接数(mysql.max_connections)

<?php
    $db = mysql_connect() ;
    $resut = mysql_query();
    // process result...
    usleep(500);
    //mysql_close($db);  let's say, you didn't call to this
    // other logic, assuming it costs 5s
    sleep(5);
    exit(0); //finish

上面的例子, 我们会保持一个和Mysql的连接5秒钟, 这样的脚本对于一般的应用来说没有关系, 但是对于一个请求量很大的脚本来说, 会导致一个致命问题:

比如一个繁忙的应用, 每秒要处理来自用户的1000个请求, 那么5秒钟请求多少个? 5 * 1000 = 5000, 而Mysql有最大连接数限制(mysql.max_connections), 这个数字一般不超过2000, 默认的会更低。

那么, 这样代码会导致你的应用, 根本无法正常提供服务. 而如果我们在对Mysql的处理完成后就关闭这个连接, 那么就不会触发这个问题。

而我们在实践中, 遇到过一个更加实际的问题, 看下面的例子:

<?PHP
$mmc = new Memcached();
$mysql = mysql_connect();
//process
mysql_close($mysql);
$mmc->close();

这是一个真实的教训, 代码如上面所示, 突然有一天我们的Mysql出现了问题, 导致连接Mysql的耗时增大, 然后就导致, 一个脚本对Memcached连接占用过长, 最后Memcache因为连接数太多, 就拒绝服务了..

所以, 我们一定要让连接代价最高的资源, 最先初始化.

系统最大句柄 (/proc/sys/fs/file-max)

这个很简单, 如果你持续打开句柄, 而不释放, 那么你有可能触发系统最大句柄限制, 对于进程来说, 自己还有进程可打开句柄数限制(ulimit -n).

系统调用是昂贵的(System call is expensive)

PHP之所以会在请求结束后正确的释放掉所有的资源, 内存, 这是因为当我们在脚本中使用新的内存的时候, PHP会向OS申请一大块内存(ZEND_MM_SEG_SIZE大小), 然后分给你你需要的合适的一块小内存。当你不使用这块小内存的时候, PHP也不会返还给OS, 而是保留下来给后续的处理使用。

所以, 如果你使用完了资源不及时释放, 那么后续的逻辑如果请求内存, PHP发现之前申请的一大块内存已经分光了, 它就只好再次向OS发起malloc调用, 得到一块新的大内存. 并且它还需要对这个大内存做一些标记处理..

而如果你使用完资源, 及时释放的话, 那么下次脚本申请内存的时候, 你之前归还的内存块就可以被重复利用, 那么也许你的整个脚本只需要和OS申请一次内存

摘自:https://www.laruence.com/2012/07/25/2662.html