Press "Enter" to skip to content

深入理解PHP7内核之Reference

问题

上一章说过引用(REFERENCE)在PHP5的时候是一个标志位, 而在PHP7以后我们把它变成了一种新的类型:IS_REFERNCE. 然而引用是一种很常见的应用, 所以这个变化带来了很多的变化, 也给我们在做PHP7开发的时候, 因为有的时候疏忽忘了处理这个类型, 而带来不少的bug.

最简单的情况, 就是在处理各种类型的时候, 从此以后我们要多考虑这种新的类型, 比如在PHP7中, 这样的代码形式就变得很常见了:

try_again:
swtich (Z_TYPE_P(zv)) {
	case IS_TRING:
	break;
	case IS_ARRAY:
	break;
    ...
	case IS_REFERENCE:
	zv = Z_REFVAL_P(zv); //解引用
	goto try_again;
	break;
}

如果大家自己写的扩展, 如果忘了考虑这种新的类型, 那么就会导致问题.

为什么?

那么既然这种新类型会带来这么多问题, 那么当时为什么要用把引用变成一种类型呢? 为什么不还是使用一个标志位呢?

一句话来说, 就是我们不得不这么做. -_#

前面说到, Hashtable直接存储的是zval, 这样在符号表中, 俩个zval如何共用一个数值呢? 对于字符串等复杂类型来说还好, 我们貌似可以在zend_refcounted结构中加入一个标志位来表明是引用来解决, 然而这个也会遇到Change On Write带来的复制, 但是我们知道在PHP7中, 一些类型是直接存储在zval中的, 比如IS_LONG, 但是引用类型是需要引用计数的, 那么对于一个是IS_LONG并且又是IS_REFERNCE的zval该如何表示呢?

为此, 我们创造了这个新的类型:

如图所示, 引用是一种新的类型:zend_reference, 对于IS_REFERNCE类型的zval, zval.value.ref是一个指向zend_reference的指针, 它包含了引用计数和一个zval, 具体的zval的值是存在zval.value.ref->val中的.

所以对于IS_LONG的引用来说, 就用一个类型是IS_REFERNCE的zval, 它指向一个zend_reference, 而这个zend_reference->val中是一个类型为IS_LONG的zval.

Change On Write

PHP采用引用计数来做简单的垃圾回收, 考虑如下的代码:

<?php
1. $val = "laruence";
2. $ref = &$val;
3. $copy = $val;
?>

$ref和$val是指向同一个zval的引用, 在PHP5的时候, 我们是通过一个引用计数为2, 并且引用标志位为1来表示这种情况, 当把$val复制给$copy(line 3)的时候, 我们发现$val是一个计数大于1的引用, 所以要产生Change on write, 也就是分离. 所以我们需要复制这个zval.

而在PHP7中, 情况就变得简单了很多, 首先在引用赋值给$ref(line 2)的时候, 生成一个IS_REFERNCE类型, 然后因为此时有俩个变量引用它所以zend_reference这个结构的引用计数zval.value.ref->gc.refcount为2.

再随后的赋值给$copy(line 3)的时候, 发现$val是一个引用, 于是让$copy指向的是zval.value.ref->val, 也就是字符串值为laruence的zval, 然后把zval的引用计数+1, 也就是zval.value.ref->val.value.str.gc.refcount为2. 并没有产生复制.

从而这就很好的解决了上一章所说的PHP5的那个经典的问题, 比如我们在PHP7下运行上一章的那个问题, 我们得到的结果是:

$ php-7.0/sapi/cli/php /tmp/1.php
Used 0.00021380008539
Used 0.00020173048281

可见确实没有发生复制, 从而不会产生任何的性能问题.

47 Comments

  1. 400 icon
    400 icon 2018-12-14

    你好,想问问你这个性能测试工具是用什么来做的,请教一下. Tks

  2. Line 98
    Line 98 2018-11-24

    能测试工具是用什2么来做的,请教一下。

  3. 韶华倾负
    韶华倾负 2018-11-13

    鸟哥,想看看PHP源码,从https://github.com/php/php-src.git clone了一份到电脑上,看了一些,自己想找的底层的方法没找到就随便看了,感觉有点瞎看,您有没什么建议能让在看源码时更有效率呢?

  4. Dean
    Dean 2018-09-26

    $a = array(1,2);
    $b = $a;
    $c = $a;
    xdebug_debug_zval(“a”); //(refcount=4, is_ref=0) 刷新之后refcount变成2
    // array (size=2)
    // 0 => (refcount=0, is_ref=0)int 1
    // 1 => (refcount=0, is_ref=0)int 2
    // 发生分离
    $b[] = 3;
    xdebug_debug_zval(“a”);//(refcount=3, is_ref=0) 刷新之后refcount变成2
    // array (size=2)
    // 0 => (refcount=0, is_ref=0)int 1
    // 1 => (refcount=0, is_ref=0)int 2

    问鸟哥一个问题,为什么数组$a的refcount为4,刷新过后又变成了2呢,下面的是3变成2,又是为什么?谢谢!

    • Dean
      Dean 2018-09-26

      我的php版本
      PHP Version 7.2.9-1+ubuntu16.04.1+deb.sury.org+1

    • ddd
      ddd 2018-11-12

      fff

      • dssf
        dssf 2018-11-12

        23423

  5. 1234
    1234 2018-09-17

    ·21341243

  6. phython
    phython 2018-09-01

    鸟哥,看你博客更新的程序,PHP是不是快死掉了?

  7. Cvv shop
    Cvv shop 2018-08-21

    Thatnks you so much for this info! It’s very helpful!

  8. Tomato
    Tomato 2018-08-02

    可以可以可以

  9. gel titan
    gel titan 2018-06-26

    时隔两年,鸟哥居然更新微博了 thank you

  10. 这样的情况下,内核是立即分离拷贝 OR 增加引用计数?思路是怎么样的。tks

  11. eva
    eva 2018-06-21

    鸟哥的前排,有点紧张 tks

  12. Gel titan
    Gel titan 2018-06-21

    时隔两年,鸟哥居然更新微博了 tks

  13. 码客
    码客 2018-05-26

    是学习php内核的好资料啊~

  14. and
    and 2018-05-20

    nb

  15. 小白two
    小白two 2018-05-16

    请讨论跟本文相关的技术问题,不要发广告。这是对博主的尊重!

  16. 5G云网络计算是中国领先的云计算公司,采用前沿云计算技术,专注打造专业的公共云计算服务,5G云主机13年老品牌:虚拟主机、域名注册、VPS主机、云服务器等,5G云怎么样!30余万个虚拟主机网站及200余万个域名用户的共同选择!

  17. JOE
    JOE 2018-05-02

    来自新西兰的PHPer,鸟哥的文章很久没看到了!

  18. owenliang1990
    owenliang1990 2018-04-26

    比较好奇的是,能不能不使用reference,而直接让一个zend_array内部嵌套自己,这样的话zend_array的refcount应该=2。

    例如:

    $a = [];
    $a[‘self’] = $a;

    这样的情况下,内核是立即分离拷贝 OR 增加引用计数?思路是怎么样的。

  19. zhanjh
    zhanjh 2018-04-24

    鸟哥好,各位大佬好,同事问我array()以及[]这两种数组定义的区别.我回答不上来.能解释一下吗?在底层方面有区别吗?还是就只是个语法糖?

  20. 小白
    小白 2018-04-14

    php5.6版本,突然服务爆卡,最后排错到系统内核问题,瞬间蒙了,求指点。(不能上图)是不是大量上传文件导致php消耗内存太多
    21.7% 【kernel】[k] __pv_queued_spin_lock_slowpath
    5.71% [kernel] [k] __do_page_fault
    5.48% libphp5.so [.] zendparse
    8核的系统 cpu 2.5HZ

    • Leo
      Leo 2018-10-08

      你好,想问问你这个性能测试工具是用什么来做的,请教一下。

  21. 肥皂锅
    肥皂锅 2018-04-14

    鸟哥的前排,有点紧张

  22. hy
    hy 2018-04-13

    鸟哥的博客终于更新了

  23. Anonymous
    Anonymous 2018-04-13

    时隔两年,鸟哥居然更新微博了

Leave a Reply

Your email address will not be published. Required fields are marked *