亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PHP中變量的實現方法

發布時間:2020-06-28 15:54:35 來源:億速云 閱讀:155 作者:元一 欄目:編程語言

這期內容當中小編將會給大家帶來有關PHP中變量的實現方法,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

變量是用于存儲信息的"容器":

與代數類似,可以給 PHP 變量賦予某個值(x=5)或者表達式(z=x+y)。

變量可以是很短的名稱(如 x 和 y)或者更具描述性的名稱(如 age、carname、totalvolume)。

PHP 變量規則:

undefined
undefined
undefined
undefined
undefined

PHP變量實現的基礎結構是zval,各種類型的實現均基于此結構實現,是PHP中最基礎的一個結構,每個PHP變量都對應一個zval,下面就看下這個結構以及PHP變量的內存管理機制。

zval結構

PHP中變量的實現方法

zval結構比較簡單,內嵌一個union類型的zend_value保存具體變量類型的值或指針,zval中還有兩個union:u1、u2:
  • u1:它的意義比較直觀,變量的類型就通過u1.type區分,另外一個值type_flags為類型掩碼,在變量的內存管理、gc機制中會用到,第三部分會詳細分析,至于后面兩個const_flagsreserved暫且不管

  • u2:這個值純粹是個輔助值,假如zval只有:valueu1兩個值,整個zval的大小也會對齊到16byte,既然不管有沒有u2大小都是16byte,把多余的4byte拿出來用于一些特殊用途還是很劃算的,比如next在哈希表解決哈希沖突時會用到,還有fe_pos在foreach會用到......

zend_value可以看出,除longdouble類型直接存儲值外,其它類型都為指針,指向各自的結構。

類型

zval.u1.type類型:

PHP中變量的實現方法

標量類型

最簡單的類型是true、false、long、double、null,其中true、false、null沒有value,直接根據type區分,而long、double的值則直接存在value中:zend_long、double,也就是標量類型不需要額外的value指針。

字符串

PHP中字符串通過zend_string表示:

PHP中變量的實現方法

  • gc:變量引用信息,比如當前value的引用數,所有用到引用計數的變量類型都會有這個結構,3.1節會詳細分析

  • h:哈希值,數組中計算索引時會用到

  • len:字符串長度,通過這個值保證二進制安全

  • val:字符串內容,變長struct,分配時按len長度申請內存

事實上字符串又可具體分為幾類:IS_STR_PERSISTENT(通過malloc分配的)、IS_STR_INTERNED(php代碼里寫的一些字面量,比如函數名、變量值)、IS_STR_PERMANENT(永久值,生命周期大于request)、IS_STR_CONSTANT(常量)、IS_STR_CONSTANT_UNQUALIFIED,這個信息通過flag保存:zval.value->gc.u.flags,后面用到的時候再具體分析。

數組

array是PHP中非常強大的一個數據結構,它的底層實現就是普通的有序HashTable,這里簡單看下它的結構,下一節會單獨分析數組的實現。

PHP中變量的實現方法

對象/資源

PHP中變量的實現方法

對象比較常見,資源指的是tcp連接、文件句柄等等類型,這種類型比較靈活,可以隨意定義struct,通過ptr指向,后面會單獨分析這種類型,這里不再多說。

引用

引用是PHP中比較特殊的一種類型,它實際是指向另外一個PHP變量,對它的修改會直接改動實際指向的zval,可以簡單的理解為C中的指針,在PHP中通過&操作符產生一個引用變量,也就是說不管以前的類型是什么,&首先會將新生成一個zval,類型為IS_REFERENCE,然后將val的value指向原來zval的value。

PHP中變量的實現方法

結構非常簡單,除了公共部分zend_refcounted_h外只有一個val,舉個示例看下具體的結構關系:

PHP中變量的實現方法

最終的結果如圖:

PHP中變量的實現方法

注意:引用只能通過&產生,無法通過賦值傳遞,比如:

PHP中變量的實現方法

$b = &$a這時候$a$b的類型是引用,但是$c = $b并不會直接將$b賦值給$c,而是把$b實際指向的zval賦值給$c,如果想要$c也是一個引用則需要這么操作:

PHP中變量的實現方法

這個也表示PHP中的引用只可能有一層不會出現一個引用指向另外一個引用的情況,也就是沒有C語言中指針的指針的概念。

內存管理

接下來分析下變量的分配、銷毀。

在分析變量內存管理之前我們先自己想一下可能的實現方案,最簡單的處理方式:定義變量時alloc一個zval及對應的value結構(ref/arr/str/res...),賦值、函數傳參時硬拷貝一個副本,這樣各變量最終的值完全都是獨立的,不會出現多個變量同時共用一個value的情況,在執行完以后直接將各變量及value結構free掉。

這種方式是可行的,而且內存管理也很簡單,但是,硬拷貝帶來的一個問題是效率低,比如我們定義了一個變量然后賦值給另外一個變量,可能后面都只是只讀操作,假如硬拷貝的話就會有多余的一份數據,這個問題的解決方案是:引用計數+寫時復制。PHP變量的管理正是基于這兩點實現的。

引用計數

引用計數是指在value中增加一個字段refcount記錄指向當前value的數量,變量復制、函數傳參時并不直接硬拷貝一份value數據,而是將refcount++,變量銷毀時將refcount--,等到refcount減為0時表示已經沒有變量引用這個value,將它銷毀即可。

PHP中變量的實現方法

引用計數的信息位于給具體value結構的gc中:

PHP中變量的實現方法

從上面的zend_value結構可以看出并不是所有的數據類型都會用到引用計數,longdouble直接都是硬拷貝,只有value是指針的那幾種類型才可能會用到引用計數。

下面再看一個例子:

$a = "hi~";$b = $a;

猜測一下變量$a/$b的引用情況。

這個不跟上面的例子一樣嗎?字符串"hi~"$a/$b兩個引用,所以zend_string1(refcount=2)。但是這是錯的,gdb調試發現上面例子zend_string的引用計數為0。這是為什么呢?

$a,$b -> zend_string_1(refcount=0,val="hi~")

事實上并不是所有的PHP變量都會用到引用計數,標量:true/false/double/long/null是硬拷貝自然不需要這種機制,但是除了這幾個還有兩個特殊的類型也不會用到:interned string(內部字符串,就是上面提到的字符串flag:IS_STR_INTERNED)、immutable array,它們的type是IS_STRINGIS_ARRAY,與普通string、array類型相同,那怎么區分一個value是否支持引用計數呢?還記得zval.u1中那個類型掩碼type_flag嗎?正是通過這個字段標識的,這個字段除了標識value是否支持引用計數外還有其它幾個標識位,按位分割,注意:type_flagzval.value->gc.u.flag不是一個值。

支持引用計數的value類型其zval.u1.type_flag包含(注意是&,不是等于)IS_TYPE_REFCOUNTED

#define IS_TYPE_REFCOUNTED          (1<<2)

下面具體列下哪些類型會有這個標識:

|     type       | refcounted |
+----------------+------------+
|simple types    |            |
|string          |      Y     |
|interned string |            |
|array           |      Y     |
|immutable array |            |
|object          |      Y     |
|resource        |      Y     |
|reference       |      Y     |

simple types很顯然用不到,不再解釋,string、array、object、resource、reference有引用計數機制也很容易理解,下面具體解釋下另外兩個特殊的類型:

  • interned string:內部字符串,這是種什么類型?我們在PHP中寫的所有字符都可以認為是這種類型,比如function name、class name、variable name、靜態字符串等等,我們這樣定義:$a = "hi~;"后面的字符串內容是唯一不變的,這些字符串等同于C語言中定義在靜態變量區的字符串:char *a = "hi~";,這些字符串的生命周期為request期間,request完成后會統一銷毀釋放,自然也就無需在運行期間通過引用計數管理內存。

  • immutable array:只有在用opcache的時候才會用到這種類型,不清楚具體實現,暫時忽略。

寫時復制

上一小節介紹了引用計數,多個變量可能指向同一個value,然后通過refcount統計引用數,這時候如果其中一個變量試圖更改value的內容則會重新拷貝一份value修改,同時斷開舊的指向,寫時復制的機制在計算機系統中有非常廣的應用,它只有在必要的時候(寫)才會發生硬拷貝,可以很好的提高效率,下面從示例看下:

$a = array(1,2);$b = &$a;$c = $a;//發生分離$b[] = 3;

最終的結果:

PHP中變量的實現方法

不是所有類型都可以copy的,比如對象、資源,實時上只有string、array兩種支持,與引用計數相同,也是通過zval.u1.type_flag標識value是否可復制的:

#define IS_TYPE_COLLECTABLE         (1<<3)
|     type       |  copyable  |
+----------------+------------+
|simple types    |            |
|string          |      Y     |
|interned string |            |
|array           |      Y     |
|immutable array |            |
|object          |            |
|resource        |            |
|reference       |            |

copyable的意思是當value發生duplication時是否需要copy,這個具體有兩種情形下會發生:

  • a.從literal變量區復制到局部變量區,比如:$a = [];實際會有兩個數組,而$a = "hi~";//interned string則只有一個string

  • b.局部變量區分離時(寫時復制):如改變變量內容時引用計數大于1則需要分離,$a = [];$b = $a; $b[] = 1;這里會分離,類型是array所以可以復制,如果是對象:$a = new user;$b = $a;$a->name = "dd";這種情況是不會復制object的,$a、$b指向的對象還是同一個

具體literal、局部變量區變量的初始化、賦值后面編譯、執行兩篇文章會具體分析,這里知道變量有個copyable的屬性就行了。

變量回收

PHP變量的回收主要有兩種:主動銷毀、自動銷毀。主動銷毀指的就是unset,而自動銷毀就是PHP的自動管理機制,在return時減掉局部變量的refcount,即使沒有顯式的return,PHP也會自動給加上這個操作。

垃圾回收

PHP變量的回收是根據refcount實現的,當unset、return時會將變量的引用計數減掉,如果refcount減到0則直接釋放value,這是變量的簡單gc過程,但是實際過程中出現gc無法回收導致內存泄漏的bug,先看下一個例子:

$a = [1];$a[] = &$a;unset($a);

unset($a)之前引用關系:

PHP中變量的實現方法

unset($a)之后:

PHP中變量的實現方法

可以看到,unset($a)之后由于數組中有子元素指向$a,所以refcount > 0,無法通過簡單的gc機制回收,這種變量就是垃圾,垃圾回收器要處理的就是這種情況,目前垃圾只會出現在array、object兩種類型中,所以只會針對這兩種情況作特殊處理:當銷毀一個變量時,如果發現減掉refcount后仍然大于0,且類型是IS_ARRAY、IS_OBJECT則將此value放入gc可能垃圾雙向鏈表中,等這個鏈表達到一定數量后啟動檢查程序將所有變量檢查一遍,如果確定是垃圾則銷毀釋放。

標識變量是否需要回收也是通過u1.type_flag區分的:

#define IS_TYPE_COLLECTABLE
|     type       | collectable |
+----------------+-------------+
|simple types    |             |
|string          |             |
|interned string |             |
|array           |      Y      |
|immutable array |             |
|object          |      Y      |
|resource        |             |
|reference       |             |

具體的垃圾回收過程這里不再介紹。

上述就是小編為大家分享的PHP中變量的實現方法了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

饶平县| 浦县| 永清县| 静海县| 丁青县| 天水市| 牟定县| 阿克陶县| 连山| 浮梁县| 封开县| 原平市| 乳山市| 阳新县| 隆安县| 南江县| 南投县| 保亭| 安龙县| 行唐县| 博湖县| 灵璧县| 根河市| 阿鲁科尔沁旗| 安达市| 平定县| 治多县| 汕头市| 土默特左旗| 固安县| 随州市| 九龙城区| 钟祥市| 白水县| 中方县| 策勒县| 宜黄县| 威信县| 乐东| 鄂托克前旗| 周宁县|