<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 3.2 引用計數 對于PHP這種需要同時處理多個請求的程序來說,申請和釋放內存的時候應該慎之又慎,一不小心便會釀成大錯。另一方面,除了要安全的申請和釋放內存外,還應該做到內存的最小化使用,因為它可能要處理每秒鐘數以千計的請求,為了提高系統整體的性能,每一次操作都應該只使用最少的內存,對于不必要的相同數據的復制則應該能免則免。我們來看下面這段PHP代碼: ````php <?php $a = 'Hello World'; $b = $a; unset($a); ```` 第一條語句執行后,PHP創建了$a這個變量,并為它申請了12B的內存來存放"hello world"這個字符串(最后加個NULL字符,你懂的)。緊接著把$a賦給了$b,并釋放掉$a; 對于PHP來說,如果每一次變量賦值都執行一次內存復制的話,那需要額外申請12B的內存來存放這個重復的數據,當然為了復制內存,還需要cpu執行某些計算,這當然會加重cpu的負載。當第三句執行后,$a被釋放了,我們剛才的設想突然變的這么滑稽,這次賦值顯得好多余哦。如果早就知道$a不用了,那我們直接讓$b用$a的內存不就行了,還賦值干嘛?如果你覺得12B沒什么,那設想下如果$a是個10M的文件內容,或者20M,是不是我們的計算機資源消耗的有點冤枉呢? 別擔心,PHP很聰明! 前面章節說過,PHP變量的名稱和值在內核中是保存在兩個不同的地方的,值是通過一個與名字毫無關系的zval結構來保存,而這個變量的名字a則保存在符號表里,兩者之間通過指針聯系著。在我們上面的例子里,$a是一個字符串,我們通過zend_hash_add把它添加到符號表里,然后又把它賦值給$b,兩者擁有相同的內容!如果兩者指向完全相同的內容,我們有什么優化措施嗎? ````c 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); //通過這個例子我們看出了,我們可以把$a和$b都指向helloval~! ```` 現在我們檢查$a和$b兩個變量,他們的值指向了"hello world"這個字符串在內存中的位置。但是在第三行:unset($a);這條語句釋放了$a。在這種情況下,unset函數并不知道$a的值同時被$b用著,所以如果它直接釋放內存,則會導致$b的值也被清空了,從而導致邏輯錯誤,甚至可能會導致系統崩潰。 呵呵,其實你心里明白,PHP不會讓上述問題發生的!回顧一下zval的四個成員value、type、is_ref__gc、refcount__gc,我們對value和type已經很熟了,現在則是后兩個成員發揮威力的時候了,這里我們主要講解refcount__gc這個成員。當一個變量被第一次創建的時候,它對應的zval結構體的refcount__gc成員的值會被初始化為1,理由很簡單,因為只有這個變量自己在用它。但是當你把這個變量賦值給別的變量時,refcount__gc屬性便會加1變成2,因為現在有兩個變量在用這個zval結構了! 以上描述轉為內核中的代碼大體如下: ````c 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); ZVAL_ADDREF(helloval); //這句很特殊,我們顯式的增加了helloval結構體的refcount zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval, sizeof(zval*), NULL); ```` 這個時候當我們再用unset刪除$a的時候,它刪除符號表里的$a的信息,然后清理它的值部分,這時它發現$a的值對應的zval結構的refcount值是2,也就是有另外一個變量在一起用著這個zval,所以unset只需把這個zval的refcount減去1就行了! ### 寫時復制機制 引用計數絕對是節省內存的一個超棒的模式!但是當我們修改$b的值,而且還需要繼續使用$a時,該怎么辦呢? ````c $a = 1; $b = $a; $b += 5; ```` 從代碼邏輯來看,我們希望語句執行后$a仍然是1,而$b則需要變成6。我們知道在第二句完成后內核通過讓$a和$b共享一個zval結構來達到節省內存的目的,但是現在第三句來了,這時$b的改變應該怎樣在內核中實現呢? 答案非常簡單,內核首先查看refcount__gc屬性,如果它大于1則為這個變化的變量從原zval結構中復制出一份新的專屬與$b的zval來,并改變其值。 ````c 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 */ return NULL; } if ((*varval)->refcount < 2) { //如果這個變量的zval部分的refcount小于2,代表沒有別的變量在用,return return *varval; } /* 否則,復制一份zval*的值 */ MAKE_STD_ZVAL(varcopy); varcopy = *varval; /* 復制任何在zval*內已分配的結構*/ zval_copy_ctor(varcopy); /* 從符號表中刪除原來的變量 * 這將減少該過程中varval的refcount的值 */ zend_hash_del(EG(active_symbol_table), varname, varname_len + 1); /* 初始化新的zval的refcount,并在符號表中重新添加此變量信息,并將其值與我們的新zval相關聯。*/ varcopy->refcount = 1; varcopy->is_ref = 0; zend_hash_add(EG(active_symbol_table), varname, varname_len + 1,&varcopy, sizeof(zval*), NULL); /* 返回新zval的地址 */ return varcopy; } ```` 現在$b變量擁有了自己的zval,并且可以自由的修改它的值了。 ### Change on Write 如果用戶在PHP腳本中顯式的讓一個變量引用另一個變量時,我們的內核是如何處理的呢? <pre> $a = 1; $b = &$a; $b += 5; </pre> 作為一個標準的PHP程序猿,我們都知道$a的值也變成6了。當我們更改$b的值時,內核發現$b是$a的一個用戶端引用,也就是所它可以直接改變$b對應的zval的值,而無需再為它生成一個新的不同與$a的zval。因為他知道$a和$b都想得到這次變化! 但是內核是怎么知道這一切的呢?簡單的講,它是通過zval的is_ref__gc成員來獲取這些信息的。這個成員只有兩個值,就像開關的開與關一樣。它的這兩個狀態代表著它是否是一個用戶在PHP語言中定義的引用。在第一條語句($a = 1;)執行完畢后,$a對應的zval的refcount__gc等于1,is_ref__gc等于0;。 當第二條語句執行后($b = &$a;),refcount__gc屬性向往常一樣增長為2,而且is_ref__gc屬性也同時變為了1! 最后,在執行第三條語句的時候,內核再次檢查$b的zval以確定是否需要復制出一份新的zval結構來,這次不需要復制,因為我們剛才上面的get_var_and_separate函數其實是個簡化版,并且少寫了一個條件: ````c /* 如果這個zval在php語言中是通過引用的形式存在的,或者它的refcount小于2,則不需要復制。*/ if ((*varval)->is_ref || (*varval)->refcount < 2) { return *varval; } ```` 這一次,盡管它的refcount等于2,但是因為它的is_ref等于1,所以也不會被復制。內核會直接的修改這個zval的值。 ### Separation Anxiety 我們已經了解了php語言中變量的復制和引用的一些事,但是如果復制和引用這兩個事件被組合起來使用了該怎么辦呢?看下面這段代碼: ````php $a = 1; $b = $a; $c = &$a; ```` 這里我們可以看到,$a,$b,$c這三個變量現在共用一個zval結構,有兩個屬于change-on-write組合($a,$c),有兩個屬于copy-on-write組合($a,$b),我們的is_ref__gc和refcount__gc該怎樣工作,才能正確的處理好這段復雜的關系呢? The answer is: 不可能!在這種情況下,變量的值必須分離成兩份完全獨立的存在!$a與$c共用一個zval,$b自己用一個zval,盡管他們擁有同樣的值,但是必須至少通過兩個zval來實現。見圖3.2【在引用時強制復制!】 <p style="text-align:center"><img src="http://www.walu.cc/phpbook/image/03fig02.jpg" /></p> 同樣,下面的這段代碼同樣會在內核中產生歧義,所以需要強制復制! ````php //上圖對應的代碼 $a = 1; $b = &$a; $c = $a; ```` 圖3.3: <p style="text-align:center"><img src="http://www.walu.cc/phpbook/image/03fig03.jpg" /></p> 需要注意的是,在這兩種情況下,$b都與原初的zval相關聯,因為當復制發生時,內核還不知道第三個變量的名字。 ## links * 3.1 [內存管理](<3.1.md>) * 3.3 [3.3 第三章總結](<3.3.md>)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看