请手动释放你的资源(Pleaserelease resources manually)
来源:互联网 发布:php教程下载 编辑:程序博客网 时间:2024/06/09 15:17
· 作者: Laruence( )
· 本文地址: http://www.laruence.com/2012/07/25/2662.html
· 转载请注明出处
我从来不认为这个问题是个问题,直到昨天.
昨天晚上的时候,我提交了一个RFC,关于引入finally到PHP,实现这个功能的出发点很简单,因为我看见不少人的需求,另外还有就是Stas说,一直只看到讨论, 没看到有人实现. 于是我就给实现了.
发到邮件组以后,一个开发组的同学NikitaPopov(nikic),表示强烈反对这个RFC,当然最初的论点他说了很多,最后我们在线讨论的时候,他表达了一个他的观点:
“PHP在请求结束后会释放所有的资源,所以我们没有必要调用fclose,或者mysql_close来释放资源, PHP会替我们做”
并且他表示,他从来都不会调用fclose,认为fclose的存在只是为了继承C函数族.
我很惊讶,我也不知道还有多少人是和他一样的想法,所以我决定写这篇文章.
在PHP5.2以前, PHP使用引用计数(Reference count)来做资源管理,当一个zval的引用计数为0的时候,它就会被释放. 虽然存在循环引用(Cycle reference),但这样的设计对于开发Web脚本来说,没什么问题, 因为Web脚本的特点和它追求的目标就是执行时间短,不会长期运行. 对于循环引用造成的资源泄露, 会在请求结束时释放掉. 也就是说, 请求结束时释放资源,是一种部补救措施(backup).
然而,随着PHP被越来越多的人使用,就有很多人在一些后台脚本使用PHP,这些脚本的特点是长期运行,如果存在循环引用, 导致引用计数无法及时释放不用的资源,则这个脚本最终会内存耗尽退出.
所以在PHP5.3以后,我们引入了GC, 也就是说, 我们引入GC是为了解决用户无法解决的问题.
这个是历史,我简单介绍下, 现在让我们回头来看开头的问题, 是不是因为PHP会在请求结束后释放所有的资源,于是我们就可以不用手动释放呢?
看一个例子:
Mysql最大连接数(mysql.max_connections)
1. <?php
2. $db =mysql_connect() ;
3. $resut =mysql_query();
4. // process result...
5. usleep(500);
6.
7. //mysql_close($db); let's say, you didn't call to this
8.
9. // other logic, assuming it costs 5s
10. sleep(5);
11.
12. exit(0);//finish
上面的例子,我们会保持一个和Mysql的连接5秒钟,这样的脚本对于一般的应用来说没有关系,但是对于一个请求量很大的脚本来说,会导致一个致命问题:
比如一个繁忙的应用,每秒要处理来自用户的1000个请求,那么5秒钟请求多少个? 5 * 1000 = 5000,而Mysql有最大连接数限制(mysql.max_connections),这个数字一般不超过2000,默认的会更低:(mysql.max_connections),
那么,这样代码会导致你的应用,根本无法正常提供服务.而如果我们在对Mysql的处理完成后就关闭这个连接,那么就不会触发这个问题.
而我们在实践中,遇到过一个更加实际的问题,看下面的例子:
1. <?PHP
2. $mmc =newMemcached();
3. $mysql =mysql_connect();
4. //process
5. mysql_close($mysql);
6. $mmc->close();
这是一个真实的教训,代码如上面所示, 突然有一天我们的Mysql出现了问题,导致连接Mysql的耗时增大,然后就导致, 一个脚本对Memcached连接占用过长,最后Memcache因为连接数太多,就拒绝服务了..
所以,我们一定要让连接代价最高的资源,最先初始化.
系统最大句柄(/proc/sys/fs/file-max)
这个很简单,如果你持续打开句柄, 而不释放, 那么你有可能触发系统最大句柄限制,对于进程来说, 自己还有进程可打开句柄数限制(ulimit -n).
系统调用是昂贵的(System call isexpensive)
PHP之所以会在请求结束后正确的释放掉所有的资源,内存, 这是因为当我们在脚本中使用新的内存的时候, PHP会向OS申请一大块内存(ZEND_MM_SEG_SIZE大小),然后分给你你需要的合适的一块小内存.
当你不使用这块小内存的时候, PHP也不会返还给OS,而是保留下来给后续的处理使用.
我们知道, malloc(3)会导致系统调用(brk(2))(当然也可能是mmap,我们此处不考虑这个细节, thanks to华裔), 而系统调用是昂贵的.
所以,如果你使用完了资源不及时释放,那么后续的逻辑如果请求内存, PHP发现之前申请的一大块内存已经分光了,它就只好再次向OS发起malloc调用,得到一块新的大内存. 并且它还需要对这个大内存做一些标记处理..
而如果你使用完资源,及时释放的话, 那么下次脚本申请内存的时候, 你之前归还的内存块就可以被重复利用,那么也许你的整个脚本只需要和OS申请一次内存.
内存峰值(Memory peak usage)
这个和上面的有一定的关系,当你使用完资源就释放,然后后续又使用这样的资源.那么PHP的内存占用会是:
资源+1 ->资源-1 -> 资源+1 -> 资源-1 (峰值是1)
而如果你是等到PHP请求结束再释放:
资源+1 ->资源 + 1 …. ->资源 -1 -> 资源 – 1 (峰值是2)
也就说,一个良好的编写的脚本可能要比一个瞎写的脚本,要省很多峰值内存..
考虑一个极端情况,对一个很繁忙的服务器来说,比如有10个PHP进程,每个PHP进程最大1G内存,而服务器只有8G内存.
结论 (conclusion)
结论很明显,我开头也说过了, 我从来不认为这个是个问题.
这里说一句,如果你买了一本PHP的书,它告诉你: “不用在PHP主动释放资源,因为PHP会帮你释放”的话,我建议你, 烧了它.
- 请手动释放你的资源(Pleaserelease resources manually)
- 请手动释放你的资源(Please release resources manually)
- 请手动释放你的资源(Please release resources manually)
- 请手动释放你的资源(Please release resources manually)
- 请手动释放你的资源
- 请手动释放你的资源
- 请手动释放你的资源
- Java7新的try-with-resources语句,自动资源释放
- 【java】手动释放资源问题
- 手动查看和删除IPC未释放的资源
- 使用try-with-resources自动释放资源
- try-with-resources 语句,自动资源释放
- try-with-resources资源自动释放特性
- Java 7 新的 try-with-resources 语句,自动资源释放
- Java 7 新的 try-with-resources 语句,自动资源释放
- Java 7 新的 try-with-resources 语句,自动资源释放 try(){} catch(){}
- Java 7 新的 try-with-resources 语句,自动资源释放
- Java 7 新的 try-with-resources 语句,自动资源释放
- Candidate
- Command——对象行为模式
- cocos2dx中的Director类中的getInstance()方法解析,即单例模式
- Java多线程之二
- 6.1 对象的构造与析构
- 请手动释放你的资源(Pleaserelease resources manually)
- 【POJ1308】【坑点过多】【并查集】
- office 2007 制作目录
- exchange2013客户端导入证书的注意事项
- 【JavaScript基础知识】——对象和数组
- DBMS实现——疯狂的开始
- 大数据里的真命题和伪命题
- 安卓 单指移动,双指缩放的 iamgeview
- 416B - Art Union (DP)