- 本文地址: https://www.laruence.com/2018/04/08/3179.html
- 转载请注明出处
问题
上一章说过引用(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
可见确实没有发生复制, 从而不会产生任何的性能问题.
Great work. Keep sharing
了解下指针原理就懂了
不错,很喜欢
在PHP7中, 一些类型是直接存储在zval中的, 比如IS_LONG
为什么都不更新了呢
和vvvugyukgg
$a = 1;
$b = &$a;
$c = $b;
$c = 2;
C 的值改变不会影响A
但是
$x = 1;
$a = [1, &$x];
$b = $a;
$c = $b;
$c[1] = 2;
A 会变成 [1,2]
而且只有$c[1] 和 $b[1]是指向同一个zval引用,其他下标的还是普通的值传递, 这是什么原理呢?
用了&后,$a[1]被污染成 IS_REFERENCE类型,COW(写时复制)只对IS_STRING和IS_ARRAY起作用。当赋值$b = $a;$c=$b;的时候,$a与$b都是IS_ARRAY,赋值的结果是$b、$c都指向与 $a同一个zval,但是 当修改$c[1]($b[1]同理),会发现这是一个IS_REFERENCE,所以不会启用COW,而是直接对zend_reference指向的zval.value直接修改,故会影响到$a[1],$b[1]
文章不错
最后,博客更新了
你必须成功。
如果你做的一切正常,我认为你应该成功。
你这是5.X版本的吧
a very detailed and meticulous lesson, it really has a lot of values, I will learn a lot thanks
这次必须学习
这下明白那个5.4的经典问题了
PHP扩展中,解引用一个数组,然后zend_hash_next_index_insert下一个Long元素,最后第一次访问正常,第二次访问就溢出了。
如果不是解引用后插入新的元素,而是修改元素,就一切正常。
目前想不出原因,有谁能给我一个提示。
看了鸟哥发的博客受益匪浅
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
Tks
$x = array(‘a’, ‘b’);
$y = &$x;
$z = $x;
xdebug_debug_zval(‘x’);
xdebug_debug_zval(‘y’);
xdebug_debug_zval(‘z’);
x: (refcount=2, is_ref=1)=array (0 => (refcount=1, is_ref=0)=’a’, 1 => (refcount=1, is_ref=0)=’b’)
y: (refcount=2, is_ref=1)=array (0 => (refcount=1, is_ref=0)=’a’, 1 => (refcount=1, is_ref=0)=’b’)
z: (refcount=3, is_ref=0)=array (0 => (refcount=1, is_ref=0)=’a’, 1 => (refcount=1, is_ref=0)=’b’)
——————
$x = “hello world”;
$y = &$x;
$z = $x;
xdebug_debug_zval(‘x’);
xdebug_debug_zval(‘y’);
xdebug_debug_zval(‘z’);
x: (refcount=2, is_ref=1)=’hello world’
y: (refcount=2, is_ref=1)=’hello world’
z: (refcount=0, is_ref=0)=’hello world’
字符串/数字类型还是做拷贝了
学习了,谢谢。Tks
鸟哥的博客终于更新了 tks
你好,想问问你这个性能测试工具是用什么来做的,请教一下. Tks
能测试工具是用什2么来做的,请教一下。
整理的真好,谢谢分享
鸟哥,想看看PHP源码,从https://github.com/php/php-src.git clone了一份到电脑上,看了一些,自己想找的底层的方法没找到就随便看了,感觉有点瞎看,您有没什么建议能让在看源码时更有效率呢?
11
PHP学习算是荒废了哎
$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,又是为什么?谢谢!
我的php版本
PHP Version 7.2.9-1+ubuntu16.04.1+deb.sury.org+1
fff
23423
test
·21341243
鸟哥,看你博客更新的程序,PHP是不是快死掉了?
Thatnks you so much for this info! It’s very helpful!
很好!
可以可以可以
优秀!
meci bop cu
学习下 tks
时隔两年,鸟哥居然更新微博了 thank you
时隔两年,鸟哥居然更新微博了 tks
鸟哥的博客终于更新了 tks
这样的情况下,内核是立即分离拷贝 OR 增加引用计数?思路是怎么样的。tks
鸟哥的前排,有点紧张 tks
时隔两年,鸟哥居然更新微博了 tks
这是要满血复活的节奏呀
是学习php内核的好资料啊~
Thank you for the information.
nb
请讨论跟本文相关的技术问题,不要发广告。这是对博主的尊重!
向laruence学习。
5G云网络计算是中国领先的云计算公司,采用前沿云计算技术,专注打造专业的公共云计算服务,5G云主机13年老品牌:虚拟主机、域名注册、VPS主机、云服务器等,5G云怎么样!30余万个虚拟主机网站及200余万个域名用户的共同选择!
来自新西兰的PHPer,鸟哥的文章很久没看到了!
比较好奇的是,能不能不使用reference,而直接让一个zend_array内部嵌套自己,这样的话zend_array的refcount应该=2。
例如:
$a = [];
$a[‘self’] = $a;
这样的情况下,内核是立即分离拷贝 OR 增加引用计数?思路是怎么样的。
鸟哥好,各位大佬好,同事问我array()以及[]这两种数组定义的区别.我回答不上来.能解释一下吗?在底层方面有区别吗?还是就只是个语法糖?
学习了,谢谢。
被鸟个的魅力深深折服,鸟哥以后家里装修要买灯饰,记得找我
学习下
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
你好,想问问你这个性能测试工具是用什么来做的,请教一下。
鸟哥的前排,有点紧张
鸟哥的博客终于更新了
时隔两年,鸟哥居然更新微博了
[…] 文章来源:http://www.laruence.com/2018/04/08/3179.html […]
沙发!!!