PHP垃圾回收机制详解
php 5.3之前使用的垃圾回收机制是单纯的“引用计数”,也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。
“引用计数”存在问题,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露;
php5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露。
php变量存在一个叫"zval"的变量容器中,"zval"变量容器包括含变量的类型和值,还包括额外的两个字节信息,分别是“is_ref”表示变量是否属于引用,“refcount”指向这个zval变量容器的变量个数。
如果你安装了xdebug,就可以用xdebug_debug_zval()显示“zval”的信息了。如下:
<?php
$str = "www.111cn.net";
xdebug_debug_zval('str');
结果:
str:
(refcount=1, is_ref=0),
string 'phpddt.com' (length=10)
只有当变量容器在”refcount“变成0时就被销毁.当你unset()一个变量时,想要的“zval”中refcount就会减1,再来说说前几天遇到的unset引用问题:
代码如下 | 复制代码 |
<?php
结果: b: (refcount=1, is_ref=0),string 'aaa' (length=3) |
继续说引用计数器问题,对于array和object符合类型情况又不一样了:
代码如下 | 复制代码 |
<?php
arr: (refcount=1, is_ref=0), array 'a' => (refcount=1, is_ref=0),string 'aaa' (length=3) 'b' => (refcount=1, is_ref=0),string 'bbb' (length=3) arr: (refcount=1, is_ref=0), array 'a' => (refcount=2, is_ref=0),string 'aaa' (length=3) 'b' => (refcount=1, is_ref=0),string 'bbb' (length=3) 'aaa' => (refcount=2, is_ref=0),string 'aaa' (length=3) |
可以看到看到原有的数组元素和新添加的数组元素关联到同一个"refcount"2的zval变量容器.这里我也只是起到抛砖引玉的作用。
上面我们只是简单的使用了unset,null,mysql_close,__destruct,xdebug_debug_zval 接着往下看
查看内存是否泄露
看是否有该释放的内存没有被释放,可以简单的通过 调用 memory_get_usage 函数查看内存使用情况来判断;memory_get_usage 函数返回的内存使用数据据说不是很准确,可以使用 php 的 xdebug 扩展来获得更准确翔实的内存使用情况。
代码如下 | 复制代码 |
class A{ class B{ for($i=0;;$i++){ } } |
上面就构造了一个会产生环状引用的例子;每次创建一个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,环就被打破了,内存泄露也就不会发生了。
3.其他:
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)脚本执行结束的影响
脚本执行结束,该脚本中使用的所有内存都会被释放,不论是否有引用环。
在php中我们要登录一般我们都是结合mysql session这两个结合实现的,下面我举一个例子来介绍php+mysql+session登录实例程序代码,有需要学习的朋友可参考。实例
代码如下 | 复制代码 |
<?php |
原理很简单,用户提交用户名与密码然后我们经过安全处理,然后再到mysql去精确比较如果相同就登录成功了。
利用php来验证日期格式我们可以使用checkdate()函数与正则表达式来验证,下面我一一给各位同学介绍了我在验证日期格式一些过程,最终得出最简单办法。1.使用正则验证日期时间格式
主要使用ereg、preg_match等php函数。
有个非常简单的,
代码如下 | 复制代码 |
$dateTime=”2010-6-4 00:00:00″; if(preg_match(“/^d{4}-d{2}-d{2} d{2}:d{2}:d{2}$/s”,$dateTime)) { echo “Yes”; }else{ echo “No”; } |
可是这个正则只验证了数字,并未对边界值进行验证,并不全面,于是有人写了这个正则
代码如下 | 复制代码 |
regex = “^((d{2}(([02468][048])|([13579][26]))[-/s]?((((0?[13578] |
丫的这简直就是神的作品。
于是我不得不放弃使用正则来验证时间格式合法性。
2.对时间字符串进行拆解分别验证日期时间格式。
主要用到checkdate等php函数。
这个方法很不错,验证也准确,可是代码写起来麻烦,需要将日期和时间分别拆解,日期使用checkdate($k[1],$k[2],$k[0])验证,时间又需要拆解逐个检查。
其实可以综合了上述两种方法来使用,操作也方便得多。
1.首先使用正则验证是否为“2011-11-07 12:30:55”这种格式。
用
代码如下 | 复制代码 |
preg_match(“/^d{4}-d{2}-d{2} d{2}:d{2}:d{2}$/s”,$dateTime) |
就可以了
2.然后使用strtotime()函数判断验证,传入日期字符串即可。
strtotime()函数默认返回指定日期时间字符串对应的UNIX时间戳。
strtotime()函数有个特点,就是如果传入日期字符串格式错误的话会返回false,而且支持各种的日期格式,非常方便。
下面我先给大家提供几种方法,供大家参考。
方法-:
代码如下 | 复制代码 |
$arr = range(1,10,1); |
结果: Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [5] => 6 [9] => 10 )
方法二:
代码如下 | 复制代码 |
$arr = range(1,10,1); |
结果:Array ( [1] => 2 [2] => 3 [3] => 4 [4] => 5 [6] => 7 [7] => 8 )
方法三:这种方法没有保留键名,供大家参考一下.
代码如下 | 复制代码 |
$arr = range(1,10,1); |
结果:Array ( [0] => 7 [1] => 4 [2] => 2 [3] => 10 [4] => 9 [5] => 6 )
在mysql中如果我们想利用PHP创建数据库和表方法很简单,我们可以直接使用mysql_query()来执行mysql的创建表命令,CREATE DATABASE database_name即可。具体如下
为了让 PHP 执行上面的语句,我们必须使用 mysql_query() 函数。此函数用于向 MySQL 连接发送查询或命令。
例子
在下面的例子中,我们创建了一个名为 “my_db” 的数据库:
代码如下 | 复制代码 |
<?php |
创建表
CREATE TABLE 用于在 MySQL 中创建数据库表。
例子
下面的例子展示了如何创建一个名为 “Persons” 的表,此表有三列。列名是 “FirstName”, “LastName” 以及 “Age”:
代码如下 | 复制代码 |
<?php $con = mysql_connect("localhost","peter","abc123"); if (!$con) { die('Could not connect: ' . mysql_error()); } // Create database if (mysql_query("CREATE DATABASE my_db",$con)) { echo "Database created"; } else { echo "Error creating database: " . mysql_error(); } // Create table in my_db database mysql_select_db("my_db", $con); $sql = "CREATE TABLE Persons ( FirstName varchar(15), LastName varchar(15), Age int )"; mysql_query($sql,$con); mysql_close($con); ?> |
向数据库表插入数据
您还可以规定希望在其中插入数据的列:
INSERT INTO table_name (column1, column2,...)
VALUES (value1, value2,....)
例子
在前面的章节,我们创建了一个名为 “Persons” 的表,有三个列:”Firstname”, “Lastname” 以及 “Age”。我们将在本例中使用同样的表。下面的例子向 “Persons” 表添加了两个新记录:
代码如下 | 复制代码 |
<?php $con = mysql_connect("localhost","peter","abc123"); if (!$con) { die('Could not connect: ' . mysql_error()); } mysql_select_db("my_db", $con); mysql_query("INSERT INTO Persons (FirstName, LastName, Age) VALUES ('Peter', 'Griffin', '35')"); mysql_query("INSERT INTO Persons (FirstName, LastName, Age) VALUES ('Glenn', 'Quagmire', '33')"); mysql_close($con); ?> |
--------------------------------------------------------------------------------
把来自表单的数据插入数据库
代码如下 | 复制代码 |
<html> |
当用户点击上例中 HTML 表单中的提交按钮时,表单数据被发送到 “insert.php”。”insert.php” 文件连接数据库,并通过 $_POST 变量从表单取回值。然后,mysql_query() 函数执行 INSERT INTO 语句,一条新的记录会添加到数据库表中。
下面是 “insert.php” 页面的代码:
代码如下 | 复制代码 |
<?php $con = mysql_connect("localhost","peter","abc123"); if (!$con) { die('Could not connect: ' . mysql_error()); } mysql_select_db("my_db", $con); $sql="INSERT INTO Persons (FirstName, LastName, Age) VALUES ('$_POST[firstname]','$_POST[lastname]','$_POST[age]')"; if (!mysql_query($sql,$con)) { die('Error: ' . mysql_error()); } echo "1 record added"; mysql_close($con) ?> |
相关文章
- 在公司经常会听到大牛们讨论时说道内存泄露神马的,每每都惊羡不已,最近精力主要用在了Web 开发上,读了一下《JavaScript高级程序设计》(书名很唬人,实际作者写的特别好,由浅入深)了解了一下JavaScript垃圾回收机制,对内存泄露...2013-10-13
- 这篇文章主要帮助大家更好的理解js回收机制,通俗易懂,便于大家理解,感兴趣的小伙伴们可以参考一下...2016-03-01
- 这篇文章主要介绍了c# 如何用好垃圾回收机制GC,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了Python析构函数__del__定义原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-11-20
- 这篇文章详细介绍了C#垃圾回收机制,有需要的朋友可以参考一下...2020-06-25
- 这篇文章主要介绍了JVM的种垃圾回收,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-08-15
- 这篇文章主要介绍了python垃圾回收机制(GC)原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-08
- 浅谈关于C#的垃圾回收机制,需要的朋友可以参考一下...2020-06-25
- 这篇文章主要介绍了C# 的析构以及垃圾回收实例分析的相关资料,需要的朋友可以参考下...2020-06-25
- 2014年开年,我的小网站就被来自福建莆田的大量垃圾IP地址疯狂的刷站,每分钟几十次的流量,写入大量垃圾评论。查了一下网上的消息,很多小博客和论坛都被狂刷,而且都是来自福建莆田...2016-01-28
- 随着时间的推移,表里面的数据越来越多,表数据文件越来越大,数据库占用的空间自然也逐渐增长,本文主要介绍了MySQL表空间回收,感兴趣的可以了解一下...2021-09-29
- 本文我们来详谈PHP垃圾回收机制,本教程是在PHP 5.3的新垃圾回收机制的特点,这种垃圾回收机制,大大改变内存泄漏问题难以解决的问题。 这部分将说明PHP 5.3的新的垃圾...2016-11-25
- PHP 可以自动进行内存管理,清除不再需要的对象。PHP使用了引用计数(reference counting)这种单纯的垃圾回收(garbage collection)机制。每个对象都内含一个引用计数器,...2016-11-25
- 本文给大家介绍的是PHP线程的内存回收问题,方便大家理解内存回收的机制,推荐给大家。...2016-07-25
- 这篇文章主要介绍了IIS 6.0 应用程序池回收和工作进程介绍,需要的朋友可以参考下...2016-01-27
- 这篇文章主要介绍了C#开发中的垃圾回收机制,需要的朋友可以参考下...2020-06-25
- 在本篇文章里小编给大家分享的是关于Python小白垃圾回收机制入门的相关知识点,需要的朋友们可以参考下。...2020-06-10
- 这篇文章主要介绍了.net非托管资源的回收方法,以实例形式详细分析归纳了.net非托管资源的两种回收方法,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
- 这篇文章主要为大家介绍了Go语言教程关于GC垃圾回收三色标记的示例详解,本篇文章是Go语言七篇入门教程系列文章,有需要的朋友可以借鉴参考下,希望能够有所帮助...2021-11-10
- C++为什么不加入垃圾回收机制呢?现在肯定还有很多人不太了解,不过没关系,下面小编就为大家详细的介绍下究竟C++为什么不加入垃圾回收机制。一起跟随小编过来看看吧...2020-04-25