ホームページ >php教程 >php手册 >PHPのガベージコレクションメカニズムの簡単な説明

PHPのガベージコレクションメカニズムの簡単な説明

WBOY
WBOYオリジナル
2016-06-13 12:14:401042ブラウズ

私も PHP の学習者ですが、これまでは、コード内で unset、null、mysql_close、__destruct などの関数を使用して、メモリ オーバーフローを防ぐためにオブジェクトを解放するだけでした。私はインターネット上の GG で次の手順を見つけて記録しました: 「PHP は自動的にメモリを管理し、不要になったオブジェクトを消去できます。PHP は参照カウントと呼ばれる単純なガベージ コレクション メカニズムを使用します。各オブジェクトに参照カウンターが含まれるたびに、各参照はオブジェクトに接続されます。参照がリビング スペースから離れるか、NULL に設定されると、カウンターは 1 ずつ減ります。オブジェクトの参照カウンターがゼロになると、PHP はこのオブジェクトがゼロになることを認識します。必要になり、それが占有しているメモリ空間が解放されます。”

众所周知, PHP 引擎本身是用 C 写的,提到 C 不能不提的就是 GC(垃圾回收).通过 PHP 手册 我们了解到, PHP 引擎会自动进行 GC 动作.那么我们不禁要问,到底它是怎么回收的, & 引用操作是不是指针, unset() 了一个变量时它是不是真的被回收了呢?这些看似手册有提及的问题,如果仔细分析会发现,远没有那么简单泛泛.也许有人会跳出来说:看 PHP 源码不就知道了.是的,等你通读了 PHP 源码后这个问题肯定不在话下了,然本篇要仅从 PHP 本身来分析这些看似平常却被忽视的小细节,当然了,其中难免水平所限,有所疏漏,热烈欢迎广大 phper 来共同讨论.

首先咱先看到例子,最简单不过的执行流程了:
Example 1: gc.php
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

echo $b ." ";
?>

不用说 % php -f gc.php 输出结果非常明了:
hy0kl% php -f gc.php
I am test.

好,下一个:
Example 2:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

$b = 'I will change?';

echo $a ." ";
echo $b ." ";
?>
执行结果依然很明显:
hy0kl% php -f gc.php
I will change?
I will change?

君请看:
Example 3:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

unset($a);

echo $a ." ";
echo $b ." ";
?>
是不是得想一下下呢?
hy0kl% php -f gc.php
Notice: Undefined variable: a in /usr/local/www/apache22/data/test/gc.php on line 8
I am test.
有点犯迷糊了吗?

君再看:
Example 4:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

unset($b);

echo $a ." ";
echo $b ." ";
?>
其实如果 Example 3 理解了,这个与之异曲同工.
hy0kl% php -f gc.php
I am test.
Notice: Undefined variable: b in /usr/local/www/apache22/data/test/gc.php on line 9

君且看:
Example 5:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

$a = null;

echo '$a = '. $a ." ";
echo '$b = '. $b ." ";
?>
猛的第一感觉是什么样的?
hy0kl% php -f gc.php
$a =
$b =
没错,这就是输出结果,对 PHP GC 已有深入理解的 phper 不会觉得有什么奇怪,说实话,当我第一次运行这段代码时很意外,却让我对 PHP GC 有更深刻的理解了.那么下面与之同工的例子自然好理解了.

Example 6:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

$b = null;

echo '$a = '. $a ." ";
echo '$b = '. $b ." ";
?>

OK,如果上面的例子的结果对看官来说无任何细节可言,那您可关闭本窗口了,欢迎有空再来!

下面我们来详细分析 GC 与引用.
1. 所有例子中,创建了一个变量,这个过程通俗一点讲:是在内存中开辟了一块空间,在里面存放了一个字符串 I am test. . PHP 内部有个符号表,用来记录各块内存引用计数,那么此时会将这块内存的引用计数 加 1,并且用一个名为 $a 的标签(变量)指向这块内存,方便依标签名来操作内存.

2. 对变量 $a 进行 & 操作,我的理解是找到 $a 所指向的内存,并为 $b 建立同样的一引用指向,并将存放字符串 I am test. 的内存块在符号表中引用计数 加 1.换言之,我们的脚本执行到这一行的时候,存放字符串 I am test. 的那块内存被引用了两次.这里要强调的是, & 操作是建立了引用指向,而不是指针, PHP 没有指针的概念!同时有人提出说类似于 UNIX 的文件软链接.可以在一定程度上这么理解: 存放字符 I am test. 的那块内存是我们的一个真实的文件,而变量 $a 与 $b 是针对真实文件建立的软链接,但它们指向的是同一个真实文件. So, 我们看到,在 Example 2 中给 $b 赋值的同时, $a 的值也跟着变化了.与通过某一软链操作了文件类似.

3. 在 Example 3 与 4 中,进行了 unset() 操作.根据实际的执行结果,可以看出: unset() 只是断开这个变量对它原先指向的内存的引用,使变量本身成为没有定义过空引用,所在调用时发出了 Notice ,并且使那块内存在符号表中引用计数 减 1,并没有影响到其他指向这块内存的变量.换言之,只有当一块内存在符号表中的引用计数为 0 时, PHP 引擎才会将这块内存回收.
PHP 手册
4.0.0 unset() became an expression. (In PHP 3, unset() would always return 1).
这意味着什么?
看看下面的代码与其结果:
error_reporting(E_ALL);
$a = 'I am test.';
$b = & $a;

unset($a);
unset($a);
unset($a);

echo '$a = '. $a ." ";
echo '$b = '. $b ." ";
?>
hy0kl% php -f gc.php

注意: /usr/local/www/ に未定義の変数がありますapache22/data/test/gc.php 10 行目
$a =
$b = 私はテストです。
最初の unset() 操作でポインターが切断されたため、後続の操作はできません。シンボル テーブル内の任意のメモリの参照カウント。

4. 例 5 と 6 から、代入 null 操作は非常に暴力的であると明確に結論付けることができます。シンボル内のメモリが 0 に設定されると、このメモリはエンジンによって自然に再利用され、他の情報を保存するためにすぐに使用されるか、または二度と使用されない可能性があります。 , いずれの場合も、そのメモリを指す元の変数はすべて、再利用されたメモリ上で操作できなくなり、そのメモリを呼び出そうとした変数は

error_reporting を返します。 (E_ALL);
$a = '私はテストです。';

$b = null; . $a . " ";
echo '$b = '." ";

if (null === $a)
echo '$a null.';
} else
{
echo '$a の型が不明です。';
}
?> php -f gc.php
$a =
$b =
$a は null

要約すると、オープンソース製品のソース コードを見ると、比較的多くの場合、大きな一時変数、または使用後に呼び出されなくなった再利用情報は集中するか null として表示されます。これは、UNIX で実際のファイルを直接強制終了するのと同等であり、そのファイルを指すすべてのソフト リンクは当然空のリンクになります。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。