-
Notifications
You must be signed in to change notification settings - Fork 0
/
php底层原理之变量(二).md
115 lines (90 loc) · 4.84 KB
/
php底层原理之变量(二).md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
上周我们从底层的角度介绍了php变量从生成->常量赋值->销毁的完整生命周期(不了解的同学可以翻看一下前面的文章[php底层原理之变量(一)](https://github.com/sdxuzheng/php-internal/blob/master/php%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E4%B9%8B%E5%8F%98%E9%87%8F%EF%BC%88%E4%B8%80%EF%BC%89.md)),但是我们留了一个思考,不知道大家有答案了没,变量之间的赋值在底层又是如何实现的呢?
## 变量之间赋值
php变量的zval结构,我们已经介绍了很多遍了,这里我们就不再多作介绍了。但是对于zval结构体中的`refcount__gc`和`is_ref__gc`字段我们一直都没有详细介绍过,而这两个字段其实是和变量之间赋值的原理有着密切的关系的。所以,我们这次从几个例子入手,了解这两个字段的变化和由此带来的原理知识
### 写时复制原理
举例:
```php
$a = "许铮的技术成长之路";
$b = $a;
xdebug_debug_zval("a", "b");
```
结果:
```php
a: (refcount=2, is_ref=0)='许铮的技术成长之路'
b: (refcount=2, is_ref=0)='许铮的技术成长之路'
```
看到这里,大家可能会比较蒙。不是变量赋值了么?应该发生值拷贝了呀?怎么两个变量的引用计数不是1,而是2呢?
那是因为,**php在设计的时候,为了节省内存,所以在变量之间赋值时,对于值相同的两个变量,会共用一块内存,也就是会在全局符号表内将变量b的变量指针指向变量a指向的同一个zval结构体,而只有当变量的zval结构发生变化时,才会发生变量容器复制的内存变化,也因此叫做`写时复制原理`**
那什么时候会发生`写时复制原理`呢?
`写时复制原理`触发时机:
**php在修改一个变量时,如果发现变量的refcount>1,则会执行变量容器的内存复制**
举例:
```php
$a = "许铮的技术成长之路";
$b = $a; //此时变量a和变量b共同指向同一个变量容器,即refcount>1
$b = "许铮的技术成长之路1" //触发写时复制机制
xdebug_debug_zval("a", "b");
```
结果:
```php
a: (refcount=1, is_ref=0)='许铮的技术成长之路'
b: (refcount=1, is_ref=0)='许铮的技术成长之路1'
```
### 写时改变原理
变量之间的赋值我们搞清楚了,那么变量和引用之间的赋值呢?我们还是通过举例来说明
举例:
```php
$a = "许铮的技术成长之路";
$b = &$a;
xdebug_debug_zval("a", "b");
```
结果:
```php
a: (refcount=2, is_ref=1)='许铮的技术成长之路'
b: (refcount=2, is_ref=1)='许铮的技术成长之路'
```
此时,我们发现,变量a和b的refcount还是2,只不过is_ref变成了1,那是因为在将变量a引用赋值给变量b时,在原变量容器上作了修改,将is_ref变成了1,且refcount+1
那如果引用赋值的基础上又发生了变量的改变了呢?
举例:
```php
$a = "许铮的技术成长之路";
$b = &$a;
$b = "许铮的技术成长之路1"
xdebug_debug_zval("a", "b");
```
结果:
```php
a: (refcount=2, is_ref=1)='许铮的技术成长之路1'
b: (refcount=2, is_ref=1)='许铮的技术成长之路1'
```
是不是觉得很神奇?变量b和变量a的值一起发生改变了~其实这是因为触发了`写时改变原理`
`写时改变原理`触发时机:
**is_ref为1的变量容器在被赋值之前,优先检查变量容器的is_ref是否等于1,如果为1,则不进行写时复制,而是在原变量容器基础上作内容修改;而如果将is_ref为1的变量容器赋值给其他变量时,则会立即触发`写时复制`**
那么如果把刚刚举得几个例子合并在一起呢?最后结果又是什么呢?
举例:
```php
$a = "许铮的技术成长之路";
$b = $a;
$c = &$a;
xdebug_debug_zval("a", "b", "c");
```
结果:
```php
a: (refcount=2, is_ref=1)='许铮的技术成长之路'
b: (refcount=1, is_ref=0)='许铮的技术成长之路'
c: (refcount=2, is_ref=1)='许铮的技术成长之路'
```
整体执行过程是这样的,当执行到第二行时,变量容器的refcount会变成2,变量a和变量b共享同一个变量容器;当执行到第三行时,因为将变量a的引用赋值给变量c,但是变量b和变量a已经共享了同一个变量容器,此时变量容器如果要发生改变,因为refcount>2,所以会发生写时复制,将变量a和变量b分离,之后将变量a引用赋值给变量c时,则会原基础上进行修改,is_ref变成1,且refcount变成2
## 思考
那么,下面的这个例子,最终结果是什么呢?欢迎大家在下方留言或私信我~
举例:
```php
$a = "许铮的技术成长之路";
$b = $a;
$c = &$a;
$d = $a;
$e = "许铮的技术成长之路1"
$a = $e;
xdebug_debug_zval("a", "b", "c", "d", "e");
```
如果你喜欢我的文章,请点赞支持我下,并欢迎关注我的专栏,每周都会有原创且有深度的文章奉上哟~