PHP 变量与垃圾收集机制

写在前面

通过阅读本文,我们将一起了解通过了解中PHP中的两种内存管理的机制,进一步加深对于PHP中变量的理解,其中包括:

  • PHP中两种垃圾收集的机制:
    • reference counting;
    • copy-on-write;
  • 变量的赋值;
  • 变量的引用;
  • 变量范围(Scope);

Copy-on-write 机制

我们先来看一下PHP中简单的变量赋值是怎么样的:

$what = "Fred";
$what = 35;
$what = array('Fred', '35', 'Wilma'); 

PHP是一种弱类型语言,变量的申明没有那么严格,因此可以随意改变值的类型,这一点相信很多读者都已经了解了;

而这个特性的具体表现就不得不提到这一节的主角: symbol table;

在PHP中,一个变量包含两部分内容:

  1. Variable name, 变量名(e.g., $name);
  2. Variable value, 变量实际的值(e.g., “Fred”);

symbol table 就是用来保存 变量名 : 变量值在内存中的地址, 这一对映射关系的变化,接着我们通过几个例子来具体看一下:

$worker = array("Fred", 35, "Wilma");
$other = $worker;

其中:

  1. $worker 在赋值成功之后, symbol table 中就有了 worker 以及 数组的内存地址 的映射关系;
  2. 接着第二行代码,将 symbol tableother 所对应的内存地址指向 worker 映射的地址,而并不会开辟新的内存空间给 $other;
variable_name variable_content_addr
worker array_addr
other worker’s address
$other[2] = "Smith"

但如果此时 $other 想要修改数组中的变量,PHP就会为 $other 分配新的地址空间来存储其指向的内容,即:

variable_name variable_content_addr
worker array_addr
other new_array_addr

这里的原因就是PHP采取了第一种垃圾收集机制: copy-on-write;

  • 即碰到多个项目拥有重复的内容时,先不进行duplicate复制,而是等到有一方需要修改数据时,才进行复制;
  • 如此便可节省内存空间;

Reference counting

PHP中的reference引用

我们先来回顾一下PHP中的 reference, PHP官网给的定义如下:

“A PHP reference is an alias, which allows two different variables to write to the same value.”

官网说PHP中的reference是一种alias,即别名,就像一个人可以有很多称呼一样,一个内存中的值也可以有很多个变量名去表示:

$big_long_variable_name = "PHP";
$short =& $big_long_variable_name;
$big_long_variable_name .= " rocks!";
print "\$short is $short\n"; # $short is PHP rocks!
print "Long is $big_long_variable_name\n"; # Long is PHP rocks!

PHP会用 '==&' 表达式来将一个变量的引用赋值给另一个变量名,这样说还是不清晰,但我们前面已经了解了 symbol table, 那么我们就很容易来表示了:

variable_name variable_content_addr
big_long_variable_name variable_addr
short variable_addr

也就是说,在 symbol table 中,两个变量名 big_long_variable_name 以及 short 都与同一个地址绑定了,如果其中的内容发生了改变,那么它们都会发生改变。

Variable Scope 变量范围

在我们介绍reference count之前,还需要一块拼图,就是变量的 Scope, 也就是变量能够作用的范围:

Local scope
  • PHP所定义的function中(包括嵌套的函数定义),所定义的变量,称为local variable,即本地变量;
  • 这些变量只能在function本地被访问;
Global scope
  • Global 全局变量就是所有在function定义之外的变量,他们可以在PHP文件的任何地方被访问到;
  • 在Function内部的Local scope中,可以使用global关键字来访问这些global变量: global $counter;
  • 也可以使用超全局变量$GLOBALS来进行访问: $GLOBAL[counter];
Static variables
  • 方法中所定义的static 变量只在同一个function中可以被访问使用;
Function parameters
  • function所传入的参数也只能在function内部被使用

Reference counting

现在我们就可以来说 Reference counting 到底是干什么的了,PHP会为每一个(为变量)开辟的内存地址都分配一个计数器,来统计这个地址有几个别名alias,即有几个 reference: 比如我们上面提到的 short 以及 big_long_variable_name 都可以根据 symbol table 映射到 variable_addr, 那么 variable_addrreference count 就是2,而一旦这个数字变成0,那么这个内存空间就会被释放。

我们还可以使用 isset() 来查看一个变量名所映射的地址是否有值;以及 unset() 来主动地解除这种映射,从而释放内存空间。

那我们之前说的 Variable scope 变量范围如何在这个机制中体现呢?

比如在下面的这个例子中:

  1. $x = 2的申明,让存储整型值 2 的地址的 reference count 加一了;
  2. f(%x)的调用,生成了一个 function parameters scope的变量,同时生成了一个 reference 引用,再使 reference count 加一;
  3. 而随着方法调用的结束, $z 被释放,而其所指向的内存却不会被释放,而只是 reference count 减一,还没有到零;
  4. 而在整个php文件执行结束之后, reference count 变为0,内存才会得到释放;
function f(& $z) {
    $y = $z;
    $z = $z + 2;
}
$x = 2;
f($x);

Reference

[1] ‘PHP: Objects and references - Manual’. https://www.php.net/manual/en/language.oop5.references.php.

[2] axiac, ‘Answer to “References are not pointer in php?”’, Stack Overflow, Sep. 28, 2017. https://stackoverflow.com/a/46462286/17534765.

[3] R. L. and K. Tatroe, ‘Programming PHP’, 1-56592-610-2. https://docstore.mik.ua/orelly/webprog/php/ch02_03.htm.

Licensed under CC BY-NC-SA 4.0
Last updated on Nov 04, 2022 21:39 CST
comments powered by Disqus
Cogito, ergo sum
Built with Hugo
Theme Stack designed by Jimmy