Home  >  Article  >  Backend Development  >  PHP下文中不再使用的变量是否会被立即回收?

PHP下文中不再使用的变量是否会被立即回收?

WBOY
WBOYOriginal
2016-06-06 20:27:551335browse

<code>
$huge_var = "Long long string variable.....";

//使用与不使用这一句$huge_var = null;

//以下有非常多的代码,但跟$huge_var无关
//非常多的代码

//代码快要结束,此时此刻,$huge_var占用的内存回收了吗?解释器在预处理的时候有无此类优化操作?
//前面$huge_var = null的使用与否对此时此刻整个PHP脚本的内存占用量有无影响?</code>

回复内容:

<code>
$huge_var = "Long long string variable.....";

//使用与不使用这一句$huge_var = null;

//以下有非常多的代码,但跟$huge_var无关
//非常多的代码

//代码快要结束,此时此刻,$huge_var占用的内存回收了吗?解释器在预处理的时候有无此类优化操作?
//前面$huge_var = null的使用与否对此时此刻整个PHP脚本的内存占用量有无影响?</code>

如果你的这个变量是一个全局变量$GLOBALS['huge_var'],如果你不手动用unset($huge_var)释放,那么只有等到脚本运行结束后才会被回收.不过就算你没有释放,运行在PHP-FPM或MOD_PHP的时候问题也不大,因为PHP-FPM在请求结束后会释放掉所有资源,所以不会产生内存泄露.但如果你编写的是CLI命令行脚本,尤其是Daemon守护进程(while(true){})的PHP脚本时,就应该注意使用unset释放掉不再使用的变量了,避免内存泄露.

<code><?php function cn_file($path) {
    $arr = file($path);
    $arr['mem'] = memory_get_usage();
    return $arr;
}
echo memory_get_usage()."\n"; // 输出 234688
$arr = cn_file('file.txt');
echo $arr['mem']."\n";        // 输出 11673232
echo memory_get_usage()."\n"; // 输出 11673408
sleep(5);                     // top查看PHP进程内存占用为 32.8 MB
unset($arr);
echo memory_get_usage()."\n"; // 输出 235072
sleep(10);                    // top查看PHP进程内存占用为 22.2 MB</code></code>

可见unset是能够释放PHP进程占用的系统内存的,特别是在释放大数组或者长字符串的时候尤其明显.

函数中的局部变量是不需要手动释放的,函数执行完成后会自动释放.函数中可以用$GLOBALS['huge_var']或者global $huge_var访问到你的全局变量.

<code><?php function cn_file($path) {
    $arr = file($path);
    return memory_get_usage();
}
echo memory_get_usage()."\n"; // 输出 234184
$mem = cn_file('file.txt');
echo $mem."\n";               // 输出 11672672
echo memory_get_usage()."\n"; // 输出 234688</code></code>

memory_get_usage 返回分配给PHP脚本的内存,单位是字节,不包括PHP进程的内存.
memory_get_peak_usage 返回分配给PHP脚本的内存的峰值.

PHP5垃圾回收机制:
http://php.net/manual/zh/features.gc.php
每个PHP变量存在一个叫"zval"的变量容器中。

<code>$a = 'new string';      //当一个变量被赋常量值时,就会生成一个zval变量容器
xdebug_debug_zval('a'); //输出 a: (refcount=1, is_ref=0)=string(10) "new string";
$b = $a;
xdebug_debug_zval('a'); //输出 a: (refcount=2, is_ref=0)=string(10) "new string"
unset($b);
xdebug_debug_zval('a'); //输出 a: (refcount=1, is_ref=0)=string(10) "new string"</code>

可见生成了类型为string和值为new string的变量容器a.
在额外的两个字节信息中,is_ref被默认设置为FALSE,因为没有任何自定义的引用生成.
refcount被设置为1,因为这里只有一个变量使用这个变量容器.
如果我们现在执行unset($a),包含类型和值的这个变量容器就会从内存中删除.

如果一个引用计数增加,它将继续被使用,当然就不再在垃圾中.
如果引用计数减少到零,所在变量容器将被清除(free).
就是说,仅仅在引用计数减少到非零值时,才会产生垃圾周期(garbage cycle).
其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾.
php.ini中默认激活循环引用收集器:
zend.enable_gc = On
PHP可以用gc_enabled判断是否循环,可以用gc_enable/gc_disable显式激活或禁用循环引用收集器.
即使在可能根缓冲区还没满时,也能调用gc_collect_cycles强制执行周期回收.
允许打开和关闭垃圾回收机制并且允许自主的初始化的原因,是由于你的应用程序的某部分可能是高时效性的.
在这种情况下,你可能不想使用垃圾回收机制.
当然,对你的应用程序的某部分关闭垃圾回收机制,是在冒着可能内存泄漏的风险,因为一些可能根也许存不进有限的根缓冲区.
因此,就在你调用gc_disable()函数释放内存之前,先调用gc_collect_cycles()函数可能比较明智.
因为这将清除已存放在根缓冲区中的所有可能根,然后在垃圾回收机制被关闭时,可留下空缓冲区以有更多空间存储可能根.

<code>if(gc_enabled()) {
    gc_collect_cycles();
    gc_disable();
}</code>

PHP内存泄露指的是你保持了对变量的引入导致GC无法收集.

PHP垃圾回收对性能的影响:
第一个是节省了内存占用空间,另一个是垃圾回收机制执行内存清理时的执行时间增加.

为什么禁用垃圾回收(GC)会给composer带来巨大的运行效率提升?
由于PHP的垃圾回收是基于引用计数的,为了能够回收循环引用的对象,会在引用计数减少但不到0的时候,试图检测并回收循环引用的孤岛对象,但当有效对象的数量及互相引用较大(比如composer中代表包、版本和互相的依赖关系)的时候,这种搜索的开销就会变得非常巨大,造成大量的CPU计算。composer在运行的时候会在内存创建大量的对象,这些对象会触发GC机制,而这些对象需要被使用,所以GC无法清除。因此,使用gc_disable禁用GC之后,能节省CPU时间,效率更高。

很少在意这些 因为脚本执行完了什么都没了 除非是一些持久化的连接
就算释放也只是现在和一会儿的区别 又不是像java一个容器跑起来没完没了

经过试验,答案是不会立即回收。

还是加上$var = null来回收内存吧,或者写成匿名变量,或者利用函数作用域。

<code>//代码快要结束了,此时此刻,$huge_var占用的内存回收了吗?
</code>

明显没有啊,这个地方你还可以继续用$huge_var这个变量,不是吗?

<code>$huge_var = null;
</code>

上面这句会把内存释放掉,如果真的是有“非常多的代码”,最好用上这句。

另外,如果$huge_var真的很大,你就该思考下,$huge_var真的是有必要的吗?
可以考虑使用PHP的“流”相关的函数。

在php中,一个进程开始执行,引擎会先申请一块内存,在运行中不够在再这个基础上申请。
中间如果有变量被unset或赋值null,它所占的内存会被回收,但是不会还给系统内存,依旧被引擎保留着,只有当这个进程结束,php所占的内存才会被释放掉。
因此在操作容易爆内存超出的时候,可以把这个进程分多个进程进行处理。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn