PHPカーネルの格納機構(分離・変更)の詳細なグラフィックコード説明

黄舟
リリース: 2023-03-06 13:06:02
オリジナル
1326 人が閲覧しました

はしがき:

ブログを読むほとんどのプログラマは、漢字がたくさん使われた記事を読みたくないかもしれませんが、この記事では主な説明として漢字が紹介されており、根気よく読んでいただければ、必ず理解できるでしょう。ほとんどの人にご褒美を!

知っているかもしれませんし、知らないかもしれませんが、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
  }
ログイン後にコピー

---------------------------------------------------------------------------------------------------

实例三:(内存是如何泄漏的)

数组变量与普通变量生成的zval非常类似,但也有很大不同

举例:


$a = $array(&#39;one&#39;);  
$a[] = &$a;  
xdebug_debug_zval(&#39;a&#39;);
ログイン後にコピー


debug_zval_dump打印出zval的结构是:

a: (refcount=2, is_ref=1)=array (
    0 => (refcount=1, is_ref=0)=&#39;one&#39;, 
    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)=&#39;one&#39;, 
    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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート