PHP 構文には、参照代入と非参照代入の 2 つの代入方法があります。
$a = 1;
$b = $a // 非参照代入;
$c = &$b; // 参照の割り当て$a = 1;
$b = $a // 非参照代入
;
$c = &$b; // 参照の割り当て;
表面的には、通常、次のように考えられます。「参照代入とは、2 つの変数が同じ変数 (実際には C の zval) に対応することを意味しますが、非参照代入とは、新しい変数 (zval) が直接生成され、その値が同時にコピーされます。」この種の考え方はほとんどの場合理解できます。 (#1)
ただし、場合によっては、次のような非常に非効率的になります: (#2)
function print_arr($arr){//非参照渡し
print_r($arr)
}
$test_arr = 配列(
'a' => 'a'、
'b' => 'b'、
'c' => 'c'、
…
);//これは比較的大きな配列です
print_arr($test_arr);//出力を実行するために print_arr 関数が初めて呼び出されるとき
print_arr($test_arr);//出力を実行するために print_arr 関数を 2 回目に呼び出します
function print_arr($arr){//非参照渡し
print_r($arr);
}
$test_arr = 配列(
'a' => 'a',
'b' => 'b',
'C' = & gt;
...
);//これは比較的大きな配列です
print_arr($test_arr);//出力を実行するために print_arr 関数が初めて呼び出されるとき
print_arr($test_arr);//出力を実行するために print_arr 関数を 2 回目に呼び出します
上記の理解 (#1) に従って、print_arr を非参照で 2 回実行すると、$test_arr とまったく同じ新しい変数が 2 つ生成され、非常に非効率になります。
実際のコードが実行されるとき、2 つの新しい変数は生成されません。なぜなら、PHP カーネルはすでに最適化に役立っているからです。
これはどのようにして達成されるのでしょうか?ここで、この記事の要点である参照カウントとコピーオンライトについて説明します。これは、参照カウントとコピーオンライトの 2 つのメカニズムを使用して最適化されます。
これら 2 つのメカニズムを紹介する前に、まず基本的な知識、つまり PHP の変数がカーネル内でどのように表現されるかを理解しましょう。
PHP で定義された変数は zval で表されます。zval の定義は Zend/zend.h で定義されます。
typedef struct _zval_struct zval;
typedef Union _zvalue_value {
長い lval; /* 長い値 */
double dval; /* 倍精度の値 */
構造体 {
char *val
整数
HashTable *ht; /* ハッシュ テーブルの値 */
zend_object_value オブジェクト
zvalue_value
struct _zval_struct {
/* 変数情報 */
zvalue_value 値; /* 値 */
zend_uint 参照カウント
zend_uchar タイプ; /* アクティブなタイプ */
zend_uchar is_ref
};
typedef struct _zval_struct zval;
typedef Union _zvalue_value {
長い lval; /* 長い値 */
double dval; /* 倍精度の値 */
構造体{
char *val;
int len;
} str;
HashTable *ht; /* ハッシュ テーブルの値 */
zend_object_value obj;
zvalue_value;
struct _zval_struct {
/* 変数情報 */
zvalue_value 値; /* 値 */
zend_uint refcount;
zend_uchar タイプ; /* アクティブなタイプ */
zend_uchar is_ref;
};
このうち、refcount と is_ref は、参照カウントとコピーオンライトの 2 つのメカニズムを実装するための基礎です。
現在の変数 refcount には参照カウントが格納されます。これは、zval が最初に作成されたときは 1 です。参照が追加されるたびに、refcount++ が追加されます。参照分離を行う場合は、refcount-- を実行します。
is_ref は、zval が参照状態であるかどうかを示すために使用されます。 zval は初期化すると 0 になり、参照ではないことを意味します。
$a;//a:refcount=1,is_ref=0,value=NULL;
$a = 1; //a:refcount=2,is_ref=0, 値=1;$b = $a; //a,b:refcount=3,is_ref=0,value=1;
$c = $a; //a,b,c:refcount=4,is_ref=0,value=1;$d = &$c; //a,b:refcount=3,is_ref=0,value=1; c,d:refcount=1, is_ref=1, value=1
$a;//a:refcount=1,is_ref=0, value=NULL;
$a = 1; //a:refcount=2,is_ref=0,value=1;
$b = $a; //a,b:refcount=3,is_ref=0,value=1;
$c = $a; //a,b,c:refcount=4,is_ref=0,value=1;
$d = &$c; //a,b:refcount=3,is_ref=0,value=1; c,d:refcount=1, is_ref=1, value=1 上のコードのコメントは、これが行が実行されると、refcountとis_refが変更されます
コピーオンライト
Php 変数は、参照カウントを通じて変数の共有データを実現します。変数の 1 つの値を変更するとどうなるでしょうか。
変数を書き込もうとするときに、その変数が指す zval が複数の変数で共有されていることがわかると、Zend は ref_count が 1 の zval をコピーし、元の zval の refcount をデクリメントします。このプロセスは「zval 分離」と呼ばれます。 」。 zend は書き込み操作が発生したときにのみコピー操作を実行することがわかり、コピー オン ライト (コピー オン ライト) とも呼ばれます
参照変数の場合、参照によって割り当てられた変数は、バンドルされているすべての変数を変更する必要があります。
$a=1;
$b=$a;
$a=1;
$b=$a;実行時のメモリ構造図:
$a=1; $b=&a;
$a=1;
$b=&a;実行時のメモリ構造図:
上記からわかるように、参照であろうと非参照であろうと、この直接代入では新しい変数は作成されません。
参照の場合のみ is_ref は 1 に設定されます。逆参照されると、is_ref は 0 に設定されます。
読み取り/書き込みレプリケーションは、変数を分離する is_ref に基づいています。
is_ref=1の場合、変数参照時に「参照中の変数分離」が行われます
$a = 1;
$b = $a;$c = &$b;
$a = 1;$b = $a;
$c = &$b;実行時のメモリ構造図:
is_ref=0の場合、非参照変数の場合は「非参照時の変数分離」を行う
$a = 1;
$b = &$a;$c = $b;
$a = 1;$b = &$a;
$c = $b;
実行時のメモリ構造図:
本当に変数の値を変更する必要がある場合にのみ、
コード (#2) をもう一度見てみると、実際には新しい変数は生成されず、$test_arr 変数が常に出力されていることがわかります。したがって、PHP で変数が参照によって渡されることはほとんどありませんが、それでもパフォーマンスの問題は発生しません。
神様のブログより抜粋