はしがき:
ブログを読むほとんどのプログラマは、漢字がたくさん使われた記事を読みたくないかもしれませんが、この記事では主な説明として漢字が紹介されており、根気よく読んでいただければ、必ず理解できるでしょう。ほとんどの人にご褒美を!
知っているかもしれませんし、知らないかもしれませんが、PHP は弱く型付けされた動的スクリプト言語です。いわゆる弱い型とは、PHP が変数の型を厳密に検証しないことを意味します (厳密に言うと、PHP は、変数を宣言するときに、保存するデータの型を明示的に示す必要はありません)。例: $a = 1; (整数) $a = "1"; (文字列)
PHP とは正確には何ですか、そしてその基礎となる実装はどのように PHP を便利にしますか?弱い型付け言語ですか?
私は最近、たくさんの本や関連するブログ情報を読み、PHP カーネルの仕組みについてたくさん学びました。
PHP は単に C 言語のクラス ライブラリであると理解されています。php.net にアクセスしてそのソース コードをダウンロードすると、まず PHP の核となるのは zend エンジンであることがわかります。 C 言語で書かれたライブラリで、低レベルの関数管理、メモリ管理、クラス管理、変数管理を処理するために使用されます。カーネル上で、彼らは多くの拡張機能を作成しましたが、そのほとんどは独立しています。オペレーティング システムの比喩を使用するには、zend
Engine はオペレーティングシステムであり、公式には多くの「アプリケーション」が提供されていますが、この「アプリケーション」はメディアプレイではなく、mysql、libxml、dom です。もちろん、zend エンジンの API に基づいて独自の拡張機能を開発することもできます。
カーネル内の PHP 変数の保存メカニズムを紹介することから始めましょう。
PHP は型付き言語です。つまり、PHP 変数はあらゆるデータ型を保存できます。しかし、PHP は C 言語で書かれており、C 言語は厳密に型指定された言語ですよね? 各変数は固定型 (1 つは強い型で変換されますが、問題がある可能性があります) を持つため、Zend エンジンでそれを行うにはどうすればよいですか? ? 任意のデータ型を変数に保存するには?以下のストレージ構造を参照してください。
Zend/zend.h ヘッダー ファイルを開くと、次の構造が見つかります Zval:
1.zval 構造
リーリー
typedef struct _zval_struct zval;
2.zend_uchar
タイプ
PHP の変数には、 4 つのスカラー型(bool、int、float、string)、 2 つの複合型(array、 オブジェクト) と 2 つの特殊なタイプ (リソースと NULL)。 zend 内では、これらの型は次のマクロに対応します (コードの場所 phpsrc/Zend/zend.h) Zend は、型の値に基づいて値のどのメンバーにアクセスするかを決定します。 利用可能な値は次のとおりです:
3.zend_uint refcount__gc
この値は、実際には変数 (またはシンボル、symbols) の数を保存するカウンターです。シンボルテーブル (シンボル table)、スコープが異なると異なるシンボル テーブルが使用されます。これについては後で説明します) は zval を指します。変数が生成されると、その refcount=1 になります。$a = $b などの一般的な代入操作では zval の refcount が 1 ずつ増加し、それに応じて unset 操作によって 1 ずつ減少します。 PHP5.3 より前では、GC の実装に参照カウント メカニズムが使用されていました。zval の refcount が 0 未満の場合、Zend エンジンは zval を指す変数が存在しないと判断し、その結果、zval が占有しているメモリ空間を解放していました。ズヴァル。しかし、物事はそれほど単純ではない場合もあります。 単純な参照カウントメカニズムでは、zval を指す変数が設定解除されている場合でも、循環参照された zval を GC で出力できないことが後でわかります (詳細については、以下の例 3 を参照)。その結果、メモリリークが発生します ( メモリー 漏れ)。
4.is_ref__gc
.
このフィールドは、変数が参照変数であるかどうかをマークするために使用されます。通常の変数の場合は値が 0、参照変数の場合は値が 1 になります。この変数は、zval の共有、分離などに影響します。これについては後で説明します。
その名前が示すように、ref_count__gc と is_ref__gc は、PHP の GC メカニズムに必要な 2 つの非常に重要なフィールドであり、これら 2 つのフィールドの値は、xdebug などのデバッグ ツールを通じて表示できます。
ここで、zval に焦点を当てて、PHP 変数の格納メカニズムが何であるかを説明します。
以前に PHPstorm Xdebug のデバッグで Xdebug のインストールを紹介しましたが、ここでは詳しく説明しません。「phpstorm+Xdebug ブレークポイントのデバッグ」を参照してください。
安装成功后,你的脚本中,可以通过xdebug_debug_zval打印Zval的信息,用法:
$var = 1; debug_zval_dump($var); $var_dup = $var; debug_zval_dump($var);
$a = 1; $b = $a; $c = $b; $d = &$c; // 在一堆非引用赋值中,插入一个引用
整个过程图示如下:
---------------------------------------------------------
$a = 1; $b = &$a; $c = &$b; $d = $c; // 在一堆引用赋值中,插入一个非引用
整个过程图示如下:
通过实例一、二,展现了,这就是PHP的copy on write写时分离机制、change
on write写时改变机制
过程:
PHP在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1,PHP就会执行一个分离的例程,
对于上面的实例一代码,当执行到第四行的时候,PHP发现$c指向的zval的refcount大于1,那么PHP就会复制一个新的zval出来,将原zval的refcount减1,并修改symbol_table,使得$a,$b和$c分离(Separation)。这个机制就是所谓的copy on write(写时复制/写时分离)。把$d指向的新zval的is_ref的值 == 1 ,这个机制叫做change on write(写时改变)
结论:
分离指的是:分离两个变量存储的zval的位置,让分开不指向同一个空间! (那如何判定是否要分离呢,依据是什么?见下边)
改变指的是,有&引用赋值时,要把新开辟的zval 的 is_ref 赋值为1
判定是否分离的条件:如果is_ref =1 或recount == 1,则不分离
if((*val)->is_ref || (*val)->refcount<2){ //不执行Separation ... ;//process }
---------------------------------------------------------------------------------------------------
举例:
$a = $array('one'); $a[] = &$a; xdebug_debug_zval('a');
debug_zval_dump打印出zval的结构是:
a: (refcount=2, is_ref=1)=array ( 0 => (refcount=1, is_ref=0)='one', 1 => (refcount=2, is_ref=1)=... )
上述输出中,…表示指向原始数组,因而这是一个循环的引用。如下图所示:
现在,我们对$a执行unset操作,这会在symbol table中删除相应的symbol,同时,zval的refcount减1(之前为2),也就是说,现在的zval应该是这样的结构:
unset($a); (refcount=1, is_ref=1)=array ( 0 => (refcount=1, is_ref=0)='one', 1 => (refcount=1, is_ref=1)=... )
(应该ref_count=1)
(unset,其实就是打断$a在 符号表(symble table) 与zval 的一个指针映射关系。)
这时,不幸的事情发生了!
Unset之后,虽然没有变量指向该zval,但是该zval却不能被GC(指PHP5.3之前的单纯引用计数机制的GC)清理掉,$a 被释放,但是$a里的$a[1]也指向了该zval,它没有被释放,导致zval的refcount均大于0。这样,这些zval实际上会一直存在内存中,直到请求结束(参考SAPI的生命周期)。在此之前,这些zval占据的内存不能被使用,便白白浪费了,换句话说,无法释放的内存导致了内存泄露。
この種のメモリリークが一度や数回だけ発生する場合は問題ありませんが、数千回のメモリリークとなると大問題です。特に、長時間実行されるスクリプト (中断することなく常にバックグラウンドで実行されるデーモンなど) では、メモリをリサイクルできないため、最終的にシステムで「利用可能なメモリがなくなる」ため、この操作は避けなければなりません。
ガベージコレクションメカニズム:
1.php は元々、メモリのリサイクルを実現するために参照カウンターを使用します。つまり、複数の PHP 変数が同じメモリを参照する可能性があります。この場合、そのうちの 1 つを設定解除してもメモリは解放されません。 例:
$a
= 1; $b = $a; unset($a);// $a によって開かれたメモリはリサイクルされません2. 変数が占有しているメモリは自動的に消去されます。 up
(静的変数は含まれません。静的変数はスクリプトのロード時に作成され、スクリプトの終了時に解放されます)、
3. 参照カウントに欠陥があり、循環参照が発生した場合、カウンタを 0 にクリアできません。
ページアクセスが終了するまでメモリ使用量が継続します。
この問題に対して、PHP5.3ではガベージコレクション機構が追加されました。詳細については、ドキュメントを参照してください: http://www.php.cn/
ガベージ コレクションのメカニズムは Lisp で最初に提案されました。ガベージ コレクションの詳細については、
。
以上がPHPカーネルの格納機構(分離・変更)の詳細なグラフィックコード説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。