ホームページ > バックエンド開発 > PHPチュートリアル > PHP の拡張機能と埋め込み -- PHP メモリ管理_PHP チュートリアル

PHP の拡張機能と埋め込み -- PHP メモリ管理_PHP チュートリアル

WBOY
リリース: 2016-07-13 17:18:44
オリジナル
896 人が閲覧しました

PHP のメモリ管理メカニズムは非常に詳細であり、この点では Java のガベージ コレクション メカニズムに似ています。 C 言語または C++ の場合、ほとんどの場合、プログラマは適用された領域を解放することしかできません。 PHP では、何千もの接続を処理する必要があるため、これらの接続を長期間維持する必要があることがよくあります。これは、C でプログラムが終了するときに、対応するメモリ ブロックがリサイクルされるという事実とは異なります。

したがって、プログラムを作成するときにメモリのリサイクルに注意を払うようにプログラマに依存するだけでは不十分です。PHP には、メモリ リークが発生しないように、接続に関連する内部メモリ管理メカニズムが必要です。

この記事では、まず PHP のメモリメカニズムを紹介します:

C 言語の空間関数 (malloc()、free()、strdup()、realloc()、calloc() など)は、PHP では異なる形式になります。


適用されたメモリを返す: プログラマーの場合、適用されたメモリをすべて返す必要があります。そうしないとメモリ リークが発生します。常に実行する必要がないプログラムでは、プロセス全体が強制終了されると、少量のメモリ リークが終了します。しかし、Web は Apache のように常に実行されています サーバーでは、小さなメモリ リークが最終的にプログラムのクラッシュの原因となります。


エラー処理の例:

エラーを処理する場合、一般的に使用されるメカニズムは、終了または終了、または重大なエラー E_ERROR が発生すると、longjmp() を使用してこのアドレスにジャンプするというものです。しかし、このアプローチでは、ほとんどの場合、メモリ リークが発生します。すべての無料操作がスキップされるためです。 (この問題は C++ にも存在します。クラスを設計するときは、コンストラクターまたはデストラクターにエラー処理関数や警告関数を決して書かないでください。同じ理由で、オブジェクトはすでに破棄または作成段階にあるため、エラー関数の処理が中断される可能性があります。このプロセスはメモリ リークを引き起こす可能性があります)。 次のコードは、この例を示しています。 この例では、
void call_function(const char *fname, int fname_len TSRMLS_DC)
{
    zend_function *fe;
    char *lcase_fname;
    /* PHP function names are case-insensitive to simplify locating them in the function tables all function names are implicitly
     * translated to lowercase
     */
    lcase_fname = estrndup(fname, fname_len);//创造一个函数名的副本
    zend_str_tolower(lcase_fname, fname_len);//都转换成小写,这样的寻找的时候很方便,这应该也是php函数表中进行函数标识的方式。
    if (zend_hash_find(EG(function_table),
            lcase_fname, fname_len + 1, (void **)&fe) == FAILURE) {?SUCCESS。这个是要在函数表里面寻找待调用的函数。
        zend_execute(fe->op_array TSRMLS_CC);
    } else {
              php_error_docref(NULL TSRMLS_CC, E_ERROR,
                         "Call to undefined function: %s()", fname); //等同于Trigger_error() 
    }
    efree(lcase_fname);
}
ログイン後にコピー
は関数呼び出し時に PHP 関数を提供します。 PHP が関数を呼び出すときは、関数テーブル (function_table) で対応する関数を見つける必要があります。検索する前に、その関数を小文字に変換する必要があります。これにより、検索の効率が向上します。 呼び出される関数が zend_hash_find 関数を通じて見つかった場合は、zend_execute を使用してその関数を呼び出します。 haunted が見つからない場合は、見つからないことを示すエラー メッセージがポップアップ表示されます。しかし、ここで問題が発生します。関数を検索するために、関数名文字列の小文字バージョンが作成されていることに注意してください。この文字列は、zend_hash_find 関数が使用されるまで使用され、一度見つからずにエラーが報告されると、必然的にこの文字列に対応するメモリ空間が見つからなくなり、メモリ リークが発生します。

したがって、php は Zend メモリ管理 を提供しており、Zend メモリ管理は ZendMM とも呼ばれます。
  • PHP でのメモリ管理はオペレーティング システムのメカニズムに似ていますが、オブジェクトは各リクエストに含まれるメモリ用です。
  • さらに、ZendMM は ini ファイルで指定された memZ も制御しますか? cHL1eK49m1lbW9yeSBsaW1pdKOsxMfDtNKyu+HJ6sfryqew3KGjPGxpPtTazbzW0LXE1+7PwsPmv7S1vcHLy/zT67LZ1/fPtc2zz+DBqs+1tcTSu7LjoaPV67bUstnX98+1zbPW0LXEserXv LXExNq05snqx+u6zcrNt8W1xLe9t6ijrHBocNbQtrzT0LbU06a1xLqvyv2ho9Xi0Km6r8r9s qKyu8rH0ru49rzytaW1xMzmu7ujrMv8w8fW0LD8uqzT0MzYtqi1xNDFz6KjrNTa1eLQqdDFz 6K1xLDv1vrPwr7NxNy5u7DRw7+49sfrx/PL+cnqx+u1xMTatOa/6b340NCx6sq2oaPV4t H5vs3E3Lm7yrXP1rbUw7+49sfrx/O1xMTatObH+NPyvfjQ0LfWsfC1xLncwO2hozxsaT7 NrMqx1NrNvNbQv7S1vcHL0ru5ssG91tbE2rTmx+vH87XEt73KvaO6cGVyc2lzdGVudLrNcGVyLXJlcXVlc3SjrLbU09pwZXJzaXN0ZW50wLTLtbLusru24Lj6z7XNs7XEx+vH877 N0rvR+cHLo6zSsr7NysfLtcrHtsDBotTaw7/Su7j2x+vH89auzeK1xKOssru74dTax+vH873hyvjWrrrzsbu72MrVoaO1q8rH09DKsbryyse38XBlcnNpc3RlbnS/ycTc0qpydW50aW1lssXE3Na qtcCjrMv50tTU2tXi1tbH6b/2z8KjrNDo0qrSu7j2ZmxhZ8C01rjKvtXi0ru146GjttTT2srHt /香港 nVmZmVyX2xlbik8bGk+cGVybWFsbG9jKGJ1ZmZlcl9sZW4sMCkgPT0gZW1hbGxvYyhidWZmZXJfbGVuKdXi1tbBqs+1ysfTw7rqtqjS5bXEt73Kvb72tqi1xDxsaT4jZGVmaW 5lIHBlbWFsbG9jKHNpemUscGVyc2lzdGVudCkgXDxsaT4KCgo8bGk+Cjx1bD4KKChwZXJz aXN0ZW50KT9tYWxsb2Moc2l6ZSk6ZW1hbGxvYyhzaXplKSkKCmZsYWc9MbHtyr7Kx3Blcn N pc3RlbnS1xKOszqowse3KvrK7ysejrL7NuPrSu7DjtcS4vcr009rH68fztcRlbWFsbG9j0rvR+cHLoaMKPGJyPgoKCjxicj4KCs/CzbzW0L/J0tS/tLW9z7XNs7XExNq05snqx+u6r8 r9 0+twaHDW0LXExNq05snqx+u6r8r9tcS21​​LHI16q7u828o7oKCjxpbWcgc3JjPQ=="http://www.Bkjia.com/uploadfile/Collfiles/20131213 /20131213091641239.jpg" alt="">
    malloc、calloc、realloc などの関数に慣れていない場合は、次に進んでください。 http://www.cppblog.com/sandywin/archive/2011/09/14/155746.html
    さらに、2 つの セーフ モード メモリ機能 があります。 void *safe_emalloc(size_t サイズ, size_t カウント, size_t addtl);
    void *safe_pemalloc(size_t サイズ、size_t カウント、size_t addtl、char 永続); 適用されるスペースは size*count + addtl です。これが存在する理由は、int 型のオーバーフローを避けるためです。


    次に、より興味深いもの、phpでの参照カウントについて話しましょう: 引用は多くの言語で見られ、多くの状況で使用されます。参照を使用すると、各変数のコピーを作成する必要がなくなる場合があるため、スペースが節約されます。 いわゆる参照カウントとは、メモリ操作エラーの可能性を回避するために、同じメモリ空間を参照する変数の数を指します。 まず、次のコード部分を見てください。
    	* {
    	*     zval *helloval;
    	*     MAKE_STD_ZVAL(helloval);
    	*     ZVAL_STRING(helloval, "Hello World", 1);
    	*     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	*     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	* }
    
    ログイン後にコピー
    このコードは、最初に zval 変数を宣言し、次に MAKE_STD_ZVAL で初期化し、次に ZVAL_STRING を使用して初期値を付加します。次に、この変数には 2 つの変数名が与えられます。 1つ目はa、2つ目はb、2つ目は間違いなく参考になることは間違いありません。しかし、このコードには問題があるはずです。問題は、zend_hash_add を使用した後に対応する参照カウントを更新しなかったことです。 zend はそのような追加の参照が追加されたことを認識しないため、メモリを解放するときに 2 つの解放が必要になる可能性があります。所以经过修改之后的正确代码如下:
    	* {
    	*     zval *helloval;
    	*     MAKE_STD_ZVAL(helloval);
    	*     ZVAL_STRING(helloval, "Hello World", 1);
    	*     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	*     <strong>ZVAL_ADDREF(helloval);</strong>//加上这个之后,就不会有重新释放同一块内存空间这样的错误了
    	*     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),
    	*                                            &helloval, sizeof(zval*), NULL);
    	* }
    
    ログイン後にコピー
    进行了ZVAL_ADDREF之后,下一次unset变量的时候,会先查看ref_count引用计数,如果=1就释放,如果>1就只是-1,并不进行内存释放。
    Copy on Write 再来看下面的这一段php代码:
    <?php
        $a = 1;
        $b = $a;
        $b += 5;
    ?>
    ログイン後にコピー
    很显然在第二行的时候b声明了一个a的引用,那么在执行完了第三行的代码之后,b增加了,a增不增加呢?很多时候可能并不想增加。所以这个时候当Zend检测到refCount>1之后,就会执行一个变量分离的操作,把原来的一块内存变成两块内存:
    zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC)
    {
        zval **varval, *varcopy;
        if (zend_hash_find(EG(active_symbol_table),
                           varname, varname_len + 1, (void**)&varval) == FAILURE) {
           /*符号表里没找到 */
           return NULL;
       }
       if ((*varval)->refcount < 2) {
           /* varname 是唯一的引用,什么也不用做 */
           return *varval;
       }
       /* 否则的话,不是唯一的引用,给zval*做一个副本 */
           MAKE_STD_ZVAL(varcopy);
           varcopy = *varval;
       /* Duplicate any allocated structures within the zval* */
           zval_copy_ctor(varcopy); //这一块是怎么拷贝的?mark 应该已经跟varval对应的varname连起来了
       /* 把varname的版本删掉,这会减少varval的引用次数 */
           zend_hash_del(EG(active_symbol_table), varname, varname_len + 1);
       /* 初始化新创造的值的引用次数,然后附给varname变量 */
           varcopy->refcount = 1;
           varcopy->is_ref = 0;
           zend_hash_add(EG(active_symbol_table), varname, varname_len + 1,
                                            &varcopy, sizeof(zval*), NULL);
       /* Return the new zval* */
       return varcopy;
    }
    ログイン後にコピー
    首先看到了两个判断语句,第一个判断语句先在符号表里面看看有没有找到相应的变量,如果没找到也就没必要分离了。第二个判断语句是看输入的变量的引用次数是不是小于2,如果是的话那就说明输入变量*varval是唯一的,也没必要分离。 否则的话肯定有引用,这个时候就要制作一个副本varcopy。这个副本会承袭varname对应的值,但是不同之处在于帮它重新申请了内存空间,重新初始化了refcount和is_ref参数。 以a、b为例,在$b+=5,执行之后,b作为varname去寻找是否有引用,发现还有一个引用a,这个时候就把b的值拷出来,然后重新申请一片空间,在重新注册为b。这样的话就是两块独立的内存块了。

    Change on Write 再看一个代码片段:
    <?php
        $a = 1;//执行完这一句之后,a变量的ref_count是1,is_ref是0
        $b = &$a;//这一句之后,变量(zval*)的ref_count是2,然后由于显示的&,is_ref为1
        $b += 5;// 这个时候在执行这一句的时候就不会有任何的分离
    ?>
    ログイン後にコピー
    如果你觉得想要a跟着b一起改变,那没有问题,只要显式的用&符号进行引用声明就可以了。这样的话is_ref标志位就会被置1. 这时候也就没必要进行内存块的分离了。所以在上面的代码中要把第二个if语句的判断更改一下:
    if ((*varval)->is_ref || (*varval)->refcount < 2) {
        /* varname is the only actual reference,
         * or it's a full reference to other variables
         * either way: no separating to be done
         */
        return *varval;
    }
    ログイン後にコピー


    再看最后一种情况,这种情况最纠结:
    <?php
        $a = 1;
        $b = $a;
        $c = &$a;
    ?>
    ログイン後にコピー
    既不是copy on write也不是change on wirte,那没办法了,只好分离一下。这里只好b独立出来了:







    对php内存管理的一些机制就说到这里,感觉php确实是一门相当神奇的语言。哈哈。

    www.bkjia.comtruehttp://www.bkjia.com/PHPjc/621625.htmlTechArticlephp对内存的管理机制相当的详尽,它在这一点上更类与java的垃圾回收机制。而对于c语言或者c大部分时候都只能由程序员自己把申请的空间...
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート