1. <output id="5rrzn"></output>

        <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>
        1. 对PHP变量的实现方式以及内存管理的梳理

          PHP技术大全 / 2019-03-15 14:56:56

          变量

          • 局部变量 PHP中局部变量分配在zend_execute_data结构上,每次执行zend_op_array都会生成一个新的zend_execute_data 局部变量通过编译时确定的编号进行读写操作

          静态变量 静态变量只会在编译时初始化,保存在zend_op_array->static_variables 这个哈希表中 静态变量通过哈希表保存,这就使得能像普通变量那样有一个固定的编号 编译时先判断zend_op_array->static_variables 是否已创建,然后将静态变量插入哈希表 

              //zend_compile_static_var_common():
          if (!CG(active_op_array)->static_variables) {
          ALLOC_HASHTABLE(CG(active_op_array)->static_variables);
          zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_P
          TR_DTOR, 0);
          }
          //插入静态变量
          zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.
          constant), value);



          例如$count与 static_variables["count"]间的关系如图所示


          • 垃圾回收 一个是引用计数这个早期就有的基本机制,refcount减到0时,?#22836;?#21464;量 这里同时也介绍下一个比较通用的写时复制机制,

           $a = 1;     
          $b = $a; // 这里变量$a 与变量$b 持有的是同一个zend_val
          $a = 2; // 这个时候变量$a的值发生了改变,而显然,让$b的值也发生同样的改变是不符?#26174;?#26399;的 所以这个时候就会发生zend_val的复制
          //另外一种情况
          $a = 1;
          $b = &$a; //当$b只有的是对$a的引用时,这两个变量始终共用同一个zend_val
          $a = 2; //这时$b的值也为2

          • 循环引用 
            引用计数机制有一个缺陷,就是碰到循环引用时,refcount无法减到0,导致变量无法?#22836;牛?#20855;体来说就是变量内部的成员引用了变量本身,比如数组中的某个元素指向了数组

           $a = [1];     
          $a[] = &$a;
          unset($a);

          针对这种情况,php引入了垃圾回收器?#21019;?#29702; 变量是否加入垃圾检查buffer并不是根据zval的类型判断的,而是与前面介绍的是否用到引用计数一样通过 zval.u1.type_flag 记录的,只有包含 IS_TYPE_COLLECTABLE 的变量才会被GC收集

          目前垃圾只会出现在array、object两种类型中,只有这两种类型的变量会出现成员引用自身的情况
          如果当变量的refcount减少后大于0,PHP并不会立即进行对这个变量进行垃圾鉴定,而是放入一个缓冲buffer中,等这个buffer满了以后(10000个值)再统一进行处理,加入buffer的是 变量zend_value的 zend_refcounted_h

          一个变量只能加入一次buffer,为了防止重复加入,变量加入后会把
          zend_refcounted_h.gc_info 置为 GC_PURPLE ,即标为紫色,下次refcount减少时
          如果发现已经加入过了则不再重复插入。


          垃圾缓存区是一个双向链表,等到缓存区满了以后则启动垃圾检查过程:遍历缓存区,再对当前变量的所有成员进行遍历,然后把成员的refcount减1(如果成员还包含子成员则也进行递归遍历,其实就是深度优先的遍历)
          最后再检查当前变量的引用,如果减为了0则为垃圾
          这个算法的原理很简单,垃圾是由于成员引用自身导致的,那么就对所有的成员减一遍引用,结果如果发现变量本身refcount变为了0则就表明其引用全部来自自身成员。

          PHP对象在内存堆栈中的分配

          对象在PHP里面和整型、浮点型一样,也是一种数据类,都是存储不同类型数据用的, 在运行的时候都要加载到内存中去用,那么对象在内存里面是怎么体现的呢?内存从逻辑上说大体上是分为4段,栈空间段、堆空间段、代码段、初始化静态段,程序里面不同的声明放在不同的内存段里面。

          数据段(data segment)通常是指用来存放程序中已初始化?#20063;?#20026;0的全局变量如:静态变量?#32479;?#37327;

          代码段(code segment / text segment)通常是指用来存放程序执行代码的一块内存区域,比如函数和方法

          栈空间段是存储占用相同空间长度并且?#21152;每?#38388;小的数据类型的地方,比如说整型1,10,100,1000,10000,100000 等等,在内存里面?#21152;每?#38388;是等长的,都是64 位4 个字节。

          (heap)数据长度不定长,而且占有空间很大的数据类型的数据放在堆内存里面的。

          栈内存是可以直接存取的,而堆内存是 不可以直接存取的内存。对于我们的对象来数就是一种大的数据类型而且是?#21152;每?#38388;不定长的类型,所以说对象是放在堆里面的,但对象名称是放在栈里面的,这样通过对象名称就可 以使用对象了。

          • PHP脚本运行的时候,那些变量被放到了栈内存,那些被保存到了堆内存?

          在PHP5的Zend Engine的实现中,所有的值都是在堆上分配空间,并且通过引用计数和垃圾收集来管理. PHP5的Zend Engine主要使用指向zval结构的指针来操作值,在很多地方甚至通过zval的二级指针来操作.

          而在PHP7的Zend Engine实现中,值是通过zval结构本身来操作(非指针). 新的zval结构直接被存放在VM[虚拟机?]的栈上,HashTable的桶里,以及属性槽里. 这样大大减少了在堆上分配和?#22836;?#20869;存的操作,还避免了对简单值的引用计数和垃圾收集.

          引用:


          PHP对象在内存堆栈中的分配 - web21 - 博客园

          《PHP7内核剖析》

          更多精彩

          敬请关注“PHP技术大全”微信公众号


          ?#35753;?#25991;章

          足球外围投注app

              1. <output id="5rrzn"></output>

                <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>

                      1. <output id="5rrzn"></output>

                        <dl id="5rrzn"><ins id="5rrzn"><strong id="5rrzn"></strong></ins></dl><dl id="5rrzn"><font id="5rrzn"><nobr id="5rrzn"></nobr></font></dl>