PHP7源碼分析之字符串簡單分析

php5.x的字符串結構:php

struct _zval_struct {
	/* Variable information */
	struct {
		char *val;
		int len;
	} str;
	zend_uint refcount__gc;
	zend_uchar type;	/* active type */
	zend_uchar is_ref__gc;
};
複製代碼

能夠看到php5的字符串是直接以結構體形式放在zval結構體中的。數組

再來看下php7的字符串結構:緩存

struct _zend_string {
	zend_refcounted_h gc;
	zend_ulong        h;                /* hash value */
	size_t            len;
	char              val[1];
};
複製代碼

php7中是單獨增長了一個zend_string結構體來表示字符串,也就是和zval分離了。安全

  • gc字段 gc字段是zend_refcounted_h結構體,其中的refcount表示引用計數
  • h字段 該字段表示緩存的字符串的哈希值,該字段僅在字符串被看成數組時纔會使用到,且同一個字符串被看成key使用時不會重複計算其哈希值
  • val字段 val字段用到了柔性數組,當結構體中僅有一個變長字段時且爲最後一個字段時,就可使用柔性數組的表示方式。

柔性數組有什麼好處呢?
在php7中,字符串初始化內存時的函數是zend_string_alloc,具體以下:bash

static zend_always_inline zend_string *zend_string_alloc(size_t len, int persistent)
{
	zend_string *ret = (zend_string *)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent);

	GC_SET_REFCOUNT(ret, 1);
	GC_TYPE_INFO(ret) = IS_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << GC_FLAGS_SHIFT);
	ZSTR_H(ret) = 0;
	ZSTR_LEN(ret) = len;
	return ret;
}
複製代碼

能夠看到,在分配內存時,用到了_ZSTR_STRUCT_SIZE(len),而後跟蹤進去能夠找到#define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1),這個_ZSTR_HEADER_SIZE實際上是#define _ZSTR_HEADER_SIZE XtOffsetOf(zend_string, val),其實到這裏就能夠看出,他分配的內存空間實際上是zend_string結構體到柔性數組的長度加上len再加上1,爲何加1呢,由於還有個結束符「\0」。php7

而至此能夠看出,柔性數組val其實佔據告終構體末尾連續的一塊內存,能夠用於存儲不定長度的字符串值。這樣,zend_string的val就和其餘字段一塊兒存儲在了同一塊連續的內存塊中,再分配、釋放內存的時候能夠把struct看成總體來處理。函數

php5中則將字符串值單獨出來用指針表示,存儲在另外一塊內存中,這就表示結構體和字符串值是分散的兩塊內存,在讀取到zval結構體所在內存後,還須要再到零一塊內存中才能讀取字符串的值。ui

php7則只須要一次內存讀取便可,節省了一次內存讀寫:spa

那爲何定義val時使用的時val[1]呢,不能定義成val[0],val[]嗎?
由於val[]、val[0]是C99標準中才合法的,也就是說之前的版本是沒有這麼個表示方式的,而val[1]在C99標準和老版本中都是有的,因此爲了兼容不一樣版本的C編譯器,使用了val[1]來表示柔性數組。同時,val[1]僅表示佔位的意思,並不佔用實際內存。

  • len字段 而最後這個len字段,php7中是8字節,php5中則是4個字節,很明顯,php7可以表示的字符串長度要比php5大得多,php5則能夠表示(2的31次方-1)的長度,而php7爲(2的63次方-1)的長度,明顯提高了不少。 使用len字段不只能夠不用每次讀取字符串長度時計算字符串長度,同時也能夠保證字符串操做的二進制安全,由於是按照長度來讀取的。
相關文章
相關標籤/搜索