ホームページ  >  記事  >  バックエンド開発  >  PHP_PHP チュートリアルでのコピーオンライトの概要

PHP_PHP チュートリアルでのコピーオンライトの概要

WBOY
WBOYオリジナル
2016-07-13 10:29:48960ブラウズ

始める前に、簡単なコードを見てみましょう:

コードをコピーします コードは次のとおりです:

$foo = $foo;
echo $foo + $bar?> ;


このコードを実行すると、数値 2 が出力されます。メモリの観点から見ると、このコードは次のように実行される可能性があります。メモリの一部を foo 変数に割り当て、そこに 1 を格納し、メモリの一部を bar 変数に割り当て、さらに 1 を格納し、最後に を計算します。結果の出力。実際、foo 変数と bar 変数は同じ値を持つため、同じメモリを使用できることがわかりました。これにより、メモリ使用量が 1 削減され、メモリの割り当てとメモリ アドレスの管理にかかる計算のオーバーヘッドもなくなりました。 。そうです、メモリ管理に関係する多くのシステムは、同じ値の共有メモリ戦略であるコピーオンライトを実装しています

多くの場合、私たちはいくつかの用語のせいで概念に対して得体の知れない恐怖を抱くことがありますが、実際には、その基本原則は非常に単純であることがよくあります。このセクションでは、PHP でのコピーオンライト戦略の実装について紹介します:

コピー オン ライト (COW とも略される) には多くのアプリケーション シナリオがあります。たとえば、Linux のプロセス コピーにおけるメモリ使用量の最適化は、C++ の STL などのさまざまなプログラミング言語アプリケーションでも同様です。 COW は一般的に使用される最適化手法であり、次のように分類できます。 リソースの遅延割り当て。リソースは、実際に必要な場合にのみ占有され、通常、コピーオンライトによりリソースの使用量が削減されます。

注: スペースを節約するために、以下では COW は「書き込み時のコピー」を意味するために使用されます。

メモリコピーの遅延最適化

, 前述したように、PHP の COW は次のように簡単に説明できます。値を代入して変数に値を代入する場合、新しい変数に格納された値を格納するために新しいメモリを申請するのではなく、単にカウンタを使用してメモリを共有します、カウンタのみが存在します、カウンタのみが存在します、カウンタのみが存在します、カウンタのみが存在します、カウンタのみが存在します、カウンタのみが存在します、カウンタのみが存在します、カウンターのみが存在します。 カウンターのみが存在します。 参照の 1 つが指す変数の値が変更されると、メモリ使用量を削減するために、値の内容を保存するために新しい領域が割り当てられます。多くのシナリオで、PHP は COW を使用してメモリを最適化します。例: 変数の複数の代入、関数パラメータの受け渡し、関数本体内の実際のパラメータの変更など。 メモリ使用量の最適化における COW の明白な役割をより簡単に確認できるように、メモリの表示例を見てみましょう:

コードをコピーします

コードは次のとおりです:$j = 1;
var_dump(memory_get_usage());

$tipi = array_fill(0, 100000) , 'php-internal');
var_dump(memory_get_usage());
var_dump(memory_get_usage()){
$j += count( $i );
}
var_dump(memory_get_usage());

//-----実行結果-----
$ php t.php
int(630904)
int(10479840)
int(10479944) )
int(10480040)



上記のコードは、通常、配列変数 $tipi が $tipi_copy に割り当てられるとき、メモリ使用量はすぐに半分に増加するわけではなく、配列変数 $tipi_copy がループされても大きな変化はありません。 $tipi_copy 変数と $tipi 変数のデータは両方ともコピーせずに同じメモリを指します。

つまり、参照を使用しない場合でも、変数に値が割り当てられた後は、変数の値を変更しない限り、データを保存するために新しいメモリが割り当てられることはありません。これに基づいて、COW がメモリ使用量を非常に効果的に制御できるいくつかのシナリオを簡単に考えることができます。つまり、関数パラメーターの転送や大きな配列のコピーなど、計算に変数を使用するだけで変数をほとんど変更しないシナリオです。変数の値を変更する必要があります。

変更された値を区切ってコピー
变 同じメモリを共有する同じ値の複数の変数はメモリ領域を節約しますが、上記の例で同じメモリの値が変化すると (または変化する可能性があり)、変数の値が変化します。メモリが変更される (または変更される可能性がある) 場合、変更された値を「分離」する必要があります。この「分離」操作が「コピー」です。

PHP では、同じ zval アドレスが複数の変数で共有されているかどうかを区別するために、Zend エンジンは識別用に ref_count と is_ref という 2 つの変数を導入します。

コードをコピーします

コードは次のとおりです:


ref_count と is_ref は zval 構造体で定義されます (第 1 章の最初のセクションを参照)
is_ref はユーザーが使用する & が必須の参照であるかどうかを識別します
ref_count は参照カウントであり、この zval が参照される変数の数を識別するために使用されます。つまり、COW の自動参照は 0 の場合に破棄されます。
これら 2 つの変数の詳細については、「第 3 章、セクション 6: 変数の割り当てと破棄の実装」を参照してください。
注: $a=$b; と $a=&$b; には、(値が変化しない場合) の違いがないことがわかります。
少し変更してみましょう 例 2: $copy の値が変更されるとどうなりますか? :

コードをコピーします

コードは次のとおりです://$tipi = array_fill(0, 3, 'php-internal');
//array_fill はここではもう使用されていません パディング、なぜですか?
$tipi[0] = 'php-internal';
$tipi[1] = 'php-internal';
var_dump(memory_get_usage());コピー = $tipi;
var_dump(memory_get_usage());

$copy[0] = 'tipi', 'copy'; ;
var_dump(memory_get_usage());

//-----実行結果-----
$ php t.php
int(629384)
tipi: (refcount=2, is_ref=0)=array (0 => (refcount=1, is_ref=0)='php-internal',
2 => (refcount= 1, is_ref=0)='php-internal')
コピー: (refcount=2、 is_ref=0)=array (0 => (refcount=1, is_ref=0)='php-internal',
=> (refcount=1, is_ref=0)='php-internal',
int( 629512) 2 =1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='php-internal', ,
2 = & gt; (Refcount = 2, is_ref = 0) = 'php-internal')
copy:(refcount = 1、is_ref = 0)= array(0 =>(refcount = 1、is_ref = 0)= 'php-internal'、
;(refcount = 2、is_ref =0)='php-内部')
int(630088)



この例では、次の機能が見つかります:

$copy = $tipi; この基本的な割り当て操作は COW のメモリの「共有」をトリガーし、メモリのコピーを生成しません。
COW の粒度は zval 構造です。PHP のすべての変数は zval に基づいているため、メモリが必要な場合、COW のスコープは zval 構造で構成されるすべての変数になります。コピーする場合、オブジェクトは最小の粒度に分割されて処理されます。これにより、オブジェクトのすべての要素をメモリ コピーに「個別にコピー」することなく、メモリ内の複雑なオブジェクトの特定の部分を変更できます。



コードをコピーします

コードは次のとおりです:

array_fill() は、配列を埋めるときに COW 戦略も使用します。これは、この例のデモに影響を与える可能性があります。興味のある方は、$PHP_SRC/ext/standard/array.c の PHP_FUNCTION(array_fill) の実装を参照してください。

xdebug_debug_zval() は xdebug 拡張機能の関数で、zend 内の変数の参照情報を出力するために使用されます。 xdebug 拡張機能がインストールされていない場合は、代わりに debug_zval_dump() を使用できます。 参考: http://www.php.net/manual/zh/function.debug-zval-dump.php

コピーオンライトを実装する

上の 3 つの例を見て、相信大家も PHP 内の COW の実現原理を理解することができます。PHP 内の COW は参照数ref_count と is_ref实现、多一变量指针、就将ref_countadd1、反之减去1、 0 に減らされると破棄されます。同様に、もう 1 つ必須の参照 & がある場合、is_ref は 1 増加され、そうでない場合は 1 減少されます。

典型的な例を次に示します:

コードをコピーします コードは次のとおりです:
$foo = 1;
$bar = $foo'); 'foo')
: (refcount=2, is_ref=0)=1
foo: (refcount=1, is_ref=0)=1



変数の章を前に紹介した後、$foo に値が割り当てられるとき、$foo 変数の値は $foo 変数によってのみ指されることがわかりました。 $foo の値が $bar に割り当てられると、PHP はメモリを $bar にコピーしませんが、$foo と $bar が同じアドレスを指すようにします。同時に、参照カウントが 1 増加し、新しい 2 になります。続いて、$bar の値を変更します。このとき、$bar 変数が指すメモリが直接必要な場合は、それに応じて $foo の値も変更されます。これは私たちが望む結果ではありません。したがって、PHP カーネルはメモリをコピーし、その値を割り当てられた値 2 に更新します (この操作は変数分離操作とも呼ばれます)。同時に、元の $foo 変数が指すメモリは によってのみ指されます。 $foo なので、参照カウントは :refcount=1 に更新されます。

単純そうに見えますが、& 演算子の存在により、実際の状況はさらに複雑になります。以下の例を参照してください:


図6.6 &演算子>によるメモリコピー分離

この例から、PHP での問題が発生しやすい & 演算子の処理がわかります。 $beauty=&$pan; の場合、両方の変数は本質的に参照型になり、その結果、一見普通の変数 $pan が & と同じように動作します。 $pan は一部の内部処理で使用され、特に配列要素内の参照変数を使用すると、問題が発生しやすくなります。 (最後の例を参照)

作業のほとんどはテキスト処理であり、変数はキャリアです。さまざまなタイプの変数が PHP のライフサイクルを通じて実行されます。変数の COW 戦略には、Zend エンジンの変数とそのメモリの処理も反映されます。ソースコードファイル関連コンテンツを参照してください: PHP_PHP チュートリアルでのコピーオンライトの概要


コードをコピーします

コードは次のとおりです:

Zend/zend_execute.c

========================== ==== ========= zend_assign_to_variable_reference(); zend_assign_to_object(); //次のマクロ定義の使用ゼンド.h
=== =====================================
#define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z) )
#define Z_SET_REFCOUNT(z, rc) (&(z))
#define Z_ISREF (Z) REF_P (& (Z))
#Z_SET_ISREF (Z) Z_SET_ISREF_P (& (Z)) を定義します
#Z_UNSET_ISREF (Z) z_isref_p (& (& (& (z)) を定義) #z_Set_isref_to (z, isref) を定義 z_Set_isref_to_p (&(z), isref)

最後に、引用符は慎重に使用してください&

前述の変数の参照カウントと PHP での参照は同じものではありません。参照は C 言語のポインタに似ており、C 命令の柔軟性や制限がありません。

PHP には予期せぬ動作が多数あり、互換性を破壊できない歴史的な理由により、当面は修正しないことを選択するものや、使用シナリオが少ないものもあります。 PHP では、これらの罠を回避することしかできません。たとえば、次の例を考えてみましょう。

参照演算子は PHP の COW 戦略の最適化につながるため、参照を使用するには、誤用を避け、理解しにくいバグを回避するために、参照の動作を明確に理解する必要があります。 PHP の参照について十分に理解していると思われる場合は、次の例を説明してみてください:

コードをコピーします コードは次のとおりです:
$foo['love'] = 1;
$bar = &$foo['love'];
$tipi = $ foo;
$tipi['love'] = '2'
echo $foo['love'];

この例では、最後に 2 が出力されます。$tipi が $foo 変数と $bar 変数の参照操作にどのような影響を与え、$foo['love'] 汚染を参照に変え、Zend が $ を汚染しないかに誰もが非常に驚くでしょう。 tipi[ 'love'] の変更により、コピー分離されたメモリが生成されます。

http://www.bkjia.com/PHPjc/769673.htmlwww.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/769673.html技術記事始める前に、簡単なコードを見てみましょう。 以下のようにコードをコピーします。 ?php //例 1 $foo = $foo; このコードを実行します。番号を取得します...
を印刷します
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。