版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、php引用计数器和垃圾回收机制谈到引用计数器和垃圾回收机制,必须得从php变量说起。总所周知,php 是一种弱类型,但具体表现在哪里,程序里面又是怎么表现的呢? php里面又是 怎样实现引用计数器的,程序如何区分变量引用和复制? php是如何对己用完的 变量进行回收,不同的php版本的不同的垃圾回收机制又是如何实现的?1. 引用计数器讲到引用计数器,不得不先说一下变量的c语言实现。如下,几个变量的结构体和联合 体:zvalue_value 联合体:typedef union _zvalue_value long ival;/* long value */double dval; struct
2、/* double value */char * val; int len; str;hash table * ht;/* hash table value */zend_object_value obj; zvalue_value;zval的结构:struct zval struct /* variable information */zvalue_value value;/* value */ zend_uint refcountgc;zend_uchar type; /* active type */zend_uchar is_refgc;zval可以看成一个容器,zvalue_valu
3、e是该容器存储变量值的联合体,refcountgc 是引用计数,记录引用数,is_ref_gc是标志这个容器是否真正的引用,type表示这个变 量的类型。zend根据type值來决定访问value的哪个成员,可川值如下:is nulln/ais long对应valuevalis_double对应 value.dvalis_string对应 value.stris array对应 value.ht1 s object对应 value .objis bool对应valueval.is_resource对应 value.ival根据这个表格可以发现两个有意思的地方:首先是php的数组其实就是一个 h
4、ashtable,这就解释了为什么php能够支持关联数组了;其次,resource就是一 个long值,它里面存放的通常是个指针、一个内部数组的index或者其它什么只有创 建者自己才知道的东西,可以将其视作一个handleo(1) 写复制(copy on write)<?php$var = "laruence"$var_dup = $var;$var = 1;?>php在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1, php就会执行一个分离的例程,对于上面的代码,当执行到第三行的时候,php发现$var 指向的zval的r
5、efcount大于1,力広php就会复制一个新的zval出来,将原zval的refcount 减1,并修改symbol_table,使得$var分离(separation)o这个机制就是所谓的 copy on write(写时复制)。题外话:写时复制技术的一个比较有名的应用是在unix类操作系统内核中,当一个进程调用fork 函数生成一个子进程的时候,父子进程拥有相同的地址空间内容,在老版本的系统中,子进程是在fork 的时候就将父进程的地址空间中的内容都拷贝一份,对于规模较大的程序这个过程可能会有着很大的开 销,更崩溃的是,很多进程在fork之后,直接在子进程中调用exec执行另外一个程序,
6、这样原来花了 大量时间从父进程复制的地址空间都还没来得及碰一下就被新的进程地址空间代替,这显然是对资源的极 大浪费,所以在后来的系统中,就使用了写时复制技术,fork之后,子进程的地址空间还是简单的指向 父进程的地址空间,只有当子进程需要写地址空间中的内容的时候,才会单独分离一份(一般以内存页为 单位)给子进程,这样就算子进程马上调用exec函数也没关系,因为根本就不需要从父进程的地址空间 中拷贝内容,这样节约了内存同时又提高了速度。(2) 写改变(change on w rit )开始在zval里面我们看到一个字段is_ref_gc,到底如何是怎样产生作用的呢? 现在我们知道,当使用变量复制
7、的吋候,php内部并不是真止的复制,而是采用指向相同 的结构来尽量节约开销。那么,对于php中的引用,那又是如何实现呢?<?php$var = "laruence"$var_ref = &$var;$var_ref = 1;?>这段代码结束以后,$var也会被间接的修改为1,这个过程称作(change on write:写时 改变)。那么ze是怎么知道,这次的复制是不需要separation的呢? 这个时候就要用到zval中的is_ref字段了: 对于上而的代码,当第二行执行以后,$var所代表的zval的refcount变为2,并且同时置 isef为1
8、。当使用引用时,php会把is_ref_gc即程序会如下判断该引用是否真实引用,php 先检查varef代表的zval的is_ref_gc字段,如果为1,则不分离,大体逻辑示意如 下:< ?phpif(*val)->is_ref | (*val)->refcount<2)不执行 separation.;/process?>(3) 两种方式到底怎样使用,啥时候使用?v ?php$var = "larue nee”;$var_dup = $var;$var_ref = & $var;?>对于上面的代码,存在一对copy on write的变量$
9、var和$var_dup,又有一对 change on write机制的变量对$var 和$varef,这个情况又是如何运作的呢?当第二行执行的时候,和前面讲过的一样,$var_dup和$var指向相同的zval, refcount 为 2。当执行第三行的吋候,php发现要操作的zval的refcount大于1,贝9, php会执行 separation,将$var_dup 分离出去,并将$var 和$varef 做 change on write 关联。 也就是,refcount=2, is_ref= 1 o(4) 数值巨大变量处理当然如果我们在php中进行参数传递的时候,是否有必要对传递内
10、容巨大的数组心存警 惕,担心内存的大量流失?v ?phpfunction countchina($persons)$count= 0;foreach($persons as $person)if($pers on'n ation' = = *ch ina')$count+ + ;return $ count;?>例如上面这段统计一群人中有多少个中国国籍的时候,如果有一个10w的人的数组要 传进去,你是否会担心被复制后传到函数里,导致内存占川瞬间翻倍?也许你会在参数那 里加一个&表示引用?php的设计者在这里有一个很巧妙的设计,引入了一个copy on wr
11、ite的概念,在上血 的函数中,如果你不去修改$perons对象的内容,并不会有复制行为发生,内存也不会 double.但是如果我想在甫数里面修改数据,便于函数后面的处理,这个时候内存会double吗? 如下:v ?phpfunction countchina($persons)$count= 0;foreach($persons as $person)if($pers on'n atio n' = = *ch ina')$pers on 'score' = 10; $count+ + ;/利用score做一些处理return $count;在上面的代码
12、中,我修改了数组的成员内容,如果你不在参数里加&的话,内存消耗 会逐步增加,在返回以前,内存的占用会double,为撒呢?因为为了满足你的write需 要,php进行了 copy,这个copy不是一次完成的,在改变第一个$persons成员的时候, 会将$persons数组和其成员的地址复制过来,并不会把所有成员内容都复制过来,随着 spersons成员的一个个被write, 个个也都被copy过来,内存占用线性增加,到循环 结束时,double 了!如果确实要修改内容,又想避免内存消耗,又不怕影响参数变量在其 他地方的使用,那就在参数里加&吧。要释放被占用的内存变量,用uns
13、et($persons)或者spersons = null都可以迅速 地释放掉,你可以使用memory_get_usage进行监控,貌似这个机制比.net体系的垃 圾回收机制要清晰,简单和高效,经常出现的out of memory无疑是.net庞大 体系和低效内存回收机制的必然结果。 unset的作用unset()并非一个函数,而是一种语言结构,这个可以通过查看编译生成的opcode看 到区别,unset对应的不是一个函数调用的opcode/幺unset到底做了什么?在unset 对应的opcode的handler中可以看到相关内容,主要的操作时从当前符号表中删除参数 中的符号,比如在全局代码
14、中执行unset($a),那么将会在全局符号表中删除a这个符号。 全局符号表是一张哈希表,建立这张表的吋候会提供一个表中的项的析构函数,当我们从符 号表中删除a的时候,会对符号a指向的项(这里是zval的指针)调用这个析构函数,这 个析构函数的主要功能是将a对应的zval的refcount减1,如果refcount变成了 0,那 么释放这个zval所以当我们调用unset的时候,不一定能释放变量所占的内存空间,只 有当这个变量对应的zval没有别的变量指向它的吋候,才会释放掉zval,否则只是对 refcount进行减1操作。2. 垃圾回收机制(1 ) php5.2 中的垃圾回收算法refer
15、ence countingphp5.2中使用的内存回收算法是大名鼎鼎的reference counting,这个算法中文 翻译叫做“引用计数”,其思想非常直观和简洁:为每个内存对象分配一个计数器,当一个内 存对象建立时计数器初始化为1 (因此此吋总是有一个变量引用此对象),以后每有一个新变 量引用此内存对象,则计数器加1,而每当减少一个引用此内存对象的变量则计数器减1, 当垃圾回收机制运作的时候,将所有计数器为0的内存对彖销毁并回收其占用的内存。而 php中内存对象就是zval,而计数器就是refcountgc。reference counting简单直观,实现方便,但却存在一个致命的缺陷,就
16、是容易造 成内存泄露。很多朋友可能已经意识到了,如果存在循环引用,那么reference counting 就可能导致内存泄露。例如下面的代码:1.<?php2.3.$a = array()4 $a = & $a;5unset ($a);6.7?>这段代码首先建立了数组a,然后让a的第一个元素按引用指向a,这吋a的zval的refcount就变为2,然后我们销毁变量a,此时a最初指向的zval的refcount为1,但 是我们再也没有办法对其进行操作,因为其形成了一个循坏自引用,如下图所示:1 wczvalurefcou nt=2t(2) php5.3 中的垃圾回收算法co
17、ncurrent cycle collection inreference counted system s首先php会分配一个固定大小的“根缓冲区”,这个缓冲区用于存放固定数量的zval,这个 数量默认是10,000,如果需要修改则需要修改源代码zend/zend_gc.c中的常量 gc_root_buffer_max_entri es 然后重新编译。由上文我们可以知道,一个zval如果有引用,要么被全局符号表中的符号引用,要么被其 它表示复杂类型的zval中的符号引用。因此在zval中存在一些可能根(root)。这里我们暂 ii不讨论php是如何发现这些可能根的,这是个很复杂的问题,总zp
18、hp有办法发现这些 可能根zval并将它们投入根缓冲区。当根缓冲区满额时,php就会执行垃圾冋收,此冋收算法如下:1、对每个根缓冲区中的根zval按照深度优先遍历算法遍历所有能遍历到的zval,并将每 个zval的refcount减1,同时为了避免对同一 zval多次减1 (因为可能不同的根能遍历到 同一个zval),每次对某个zval减1后就对其标记为“已减”。2、再次对每个缓冲区中的根zval深度优先遍历,如果某个zval的refcount不为0,则 对其加1,否则保持其为0。3、清空根缓冲区中的所有根(注意是把这些zval从缓冲区中清除而不是销毁它们),然后销 毁所有refcount为0的zval,并收回其内存。如果不能完全理解也没有关系,只需记住php5.3的垃圾回收算法有以下几点特性:1、并不是每次refcount减少时都进入回收周期,只有根缓冲区满额后在开始垃圾冋收。2、对以解决循环引用问题。3、可以总将内存泄露保持在一个阈值以下。(3)php5.2和php5.3性能比较可以看到在可能引发累积性内存泄露的场景下,php5.2发生持续累积性内存泄露,而 php5.3则总能将内存泄露控制在一个阈值以下(与根缓
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 技术服务合同协议成功秘诀
- 轴流风机购销合同模板
- 玩具联盟采购合同样本
- 个人教育贷款居间服务合同
- 酒店用品采购协议
- 借款合同格式与注意事项
- 企业培训管理服务合同
- 装修合同解除协议书编写
- 个人安全我保证
- 老年人居家养护合同
- 幼儿园班级幼儿图书目录清单(大中小班)
- 烈士陵园的数字化转型与智能服务
- 医院与陪护公司的协议范文
- 古琴介绍(英文)(部编)课件
- DL-T5704-2014火力发电厂热力设备及管道保温防腐施工质量验收规程
- 2024年山东省烟台市中考道德与法治试题卷
- 女性生殖健康与疾病智慧树知到期末考试答案章节答案2024年山东中医药大学
- (高清版)JGT 225-2020 预应力混凝土用金属波纹管
- 2023-2024学年四川省绵阳市九年级上册期末化学试题(附答案)
- 心电图进修汇报
- 中医科进修总结汇报
评论
0/150
提交评论