你写了一个PHP脚本,一般都不用考虑内存泄露和垃圾回收的问题,因为一般情况下你的脚本很快就执行完退出了。

但在一些运行时间长,数据量大的时候,程序运行一段时间后,php脚本就占用了过多内存,然后就报错(PHP Fatal error: Allowed memory size of 134217728 bytes exhausted)退出了。一般来说,每个页面处理结束,新建的simple_html_dom对象就应该被销毁了——但是实际上没有,很明显,内存泄露发生了。

PHP的垃圾回收机制

PHP5.3之前使用的垃圾回收机制是单纯的“引用计数”,也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。

“引用计数”存在问题,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露。

PHP5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露。

查看内存是否泄露

看是否有该释放的内存没有被释放,可以简单的通过 调用  memory_get_usage  函数查看内存使用情况来判断;memory_get_usage 函数返回的内存使用数据据说不是很准确,可以使用  php 的 xdebug 扩展来获得更准确翔实的内存使用情况。

class A{
private $b;
function __construct(){
$this->b = new B($this);
}
function __destruct(){
//echo "A destruct\n";
}
} class B{
private $a;
function __construct($a){
$this->a = $a;
}
function __destruct(){
//echo "B descturct\n";
}
} for($i=0;;$i++){
$a = new A();
if($i00 == 0){
echo memory_get_usage()."\n";
}
}

上面就构造了一个会产生环状引用的例子。每次创建一个A对象的实例a,a就创建一个B对象的实例b,同时让b引用a。这样,每个A对象永远被一个B引用,而每个B对象同时被一个对象A引用,引用环就这样产生了。

在PHP5.2的环境下执行这段代码,会发现内存使用在单调上涨,也没有A和B的析构函数被执行后输出的“A/B desctruct”信息;直到内存耗尽,输出“PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 40 bytes)”。

在PHP5.3的环境下执行这段代码,则发现内存使用在上跳下窜,但是永远没有超过一个限额。程序也会输出大量的“A/B desctruct”,这说明析构函数被调用了。

我的同事的程序中,就存在这种引用的环路,而他的脚本,实在php5.2.3下执行的。simple_html_dom工具中,有两个类,分别是simple_html_dom和simple_html_dom_node,前者中有一个数组成员变量nodes,数组中每个元素都是一个simple_html_dom_node对象;而每个simple_html_dom_node对象都有一个成员变量dom,该dom的值就是前面的simple_html_dom对象——这样就形成了一个漂亮的引用环,导致了内存泄露。解决的办法也很简单,就是simple_html_dom对象在使用完毕时,主动调用其clear函数,清空其成员变量nodes,环就被打破了,内存泄露也就不会发生了。

其他

1. 垃圾回收的时机

PHP中,引用计数为0,则内存立刻释放。也就是说,不存在环状引用的变量,离开变量的作用域,内存被立刻释放。环状引用检测则是在满足一定条件下触发,所以在上面的例子中,会看到使用的内存有大幅度的波动。也可以通过 gc_collect_cycles 函数来主动进行环状引用检测。

2. &符号的影响

显式引用一个变量,会增加该内存的引用计数:

$a = "something";
$b = &$a;

此时unset($a), 但是仍有$b指向该内存区域的引用,内存不会释放。

3. unset函数的影响

unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;在上面的例子中,循环体内部,$a=new A(); unset($a);并不会将$a的引用计数减到零;

4. = null 操作的影响

$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0。

5. 脚本执行结束的影响

脚本执行结束,该脚本中使用的所有内存都会被释放,不论是否有引用环。

原文地址:http://blog.snsgou.com/post-181.html

PHP的内存泄露问题与垃圾回收的更多相关文章

  1. JVM内存管理和JVM垃圾回收机制

    JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...

  2. JVM内存管理机制和垃圾回收机制

    JVM内存管理机制和垃圾回收机制 JVM结构 图片描述: java源码编译成class文件 class文件通过类加载器加载到内存 其中方法区存放的是运行时的常量.静态变量.类信息等,被所有线程共享 堆 ...

  3. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  4. java内存机制和GC垃圾回收机制

    Java 内存区域和GC机制 转载来源于:https://www.cnblogs.com/zhguang/p/3257367.html 感谢 目录 Java垃圾回收概况 Java内存区域 Java对象 ...

  5. 内存缓存机制and垃圾回收机制

    一.内存缓存机制 var_dump(memory_get_usage(true)); $a="laruence"; var_dump(memory_get_usage(true)) ...

  6. 内存分析_.Net垃圾回收介绍

    垃圾回收 1.       .Net垃圾回收中涉及的名称 1.1.什么是代? 垃圾回收器为了提升性能使用了代的机制,共分为三代(Gen0.Gen1.Gen2).GC工作机制基于以下假设, 1)  对象 ...

  7. JVM·垃圾收集器与内存分配策略之垃圾回收算法!

    1.垃圾回收算法    1.1.标记-清除算法(Mark-Sweep):             过程分为“标记”和“清除”两个过程.先将所有需要回收的目标统一标记,然后再统一清除.          ...

  8. JVM内存区域划分及垃圾回收

    第一部分.闲扯+概述 近来在研读<深入理解java虚拟机>一书,读完之后做个小结,算是记录一下自己的学习所得,在成长的路上,只能死磕. 要理解JVM,就要先从其内存区域划分开始,知道其由几 ...

  9. Java JVM运行时数据区,内存管理和GC垃圾回收

    一 . 运行时数据区 程序计数器是线程私有的,是一块很小的内存空间,是当前线程执行到字节码行号的计数指示器.每个CPU处理器核心 在任何一个时刻,都只可能运行着唯一的一个线程,执行着一条指令.所以在多 ...

随机推荐

  1. [Alpha]团队任务拆解

    要求 团队任务拆解 Alpha阶段总体规划 初步实现测试.报告: 实现对游戏最基本的测试,包括内置随机测试.提供可供选择的组合测试 实现对游戏测试时操作的记录并最终生成报告 能够在发现异常时及时将异常 ...

  2. java的MethodHandle类详解

    一.总述   java7为间接调用方法提供了MethodHandle类,即方法句柄.可以将其看作是反射的另一种方式. 这是使用MethodHandle调用方法的一个例子: public class T ...

  3. 遇见Navicat 2003-can't connect to MYSQL server on 'localhost'(10061)

    学习过程中难免遇到问题,今天就遇到了Navicat 2003-can't connect to MYSQL server on 'localhost'(10061),navicat报错,我就纳闷以前都 ...

  4. LARTC

    大牛的博客 howto ,however, is simplify. another space ip link list ip address show ip route show route -n ...

  5. dubbo SPI设计

    SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时, ...

  6. 【HADR】How to reestablish HADR from scratch after a failure on Standby

    转载 http://www-01.ibm.com/support/docview.wss?uid=swg21514783 Cause  Have a HADR pair with Primary on ...

  7. Java Service Wrapper简介与使用(转)

    本文转自https://www.cnblogs.com/zcy_soft/p/3738947.html,写的非常好,珍藏一下 1. wrapper的意思? wrapper在此处理解为“包装”. 2. ...

  8. cookies session filter 自动登录

    webxml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=" ...

  9. 四大组件之BroadcastReceiver

    BroadcastReceiver,顾名思义就是“广播接收者”的意思,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息.它可以接收来自系统和应用的的广播 ...

  10. css3记事

    1.文字超出省略 text-overflow: ellipsis white-space: nowrap; overflow: hidden; text-overflow: ellipsis; *父元 ...