php原始碼02 -基本變數與記憶體管理機制

aa111111發表於2022-03-17

概述

這是原始碼php7系列的第二篇文章,主要介紹變數的機制和記憶體的管理,我相信學習原始碼是對程式碼整體提升的有效手段,話不多說,開始吧!


php7編譯安轉、新特性

變數實現

1. 解密zval


zval 底層結構:


struct_zval_struct {

    zend_value value; //8個位元組

    union u1; //4個位元組

    union u2; //4個位元組

}

1

2

3

4

5

對於vue來說是一個聯合體,zval一共16個位元組,u1 4個位元組,u2四個位元組,value結構體如下:


typedef union _zend_value {

zend_long         lval; /* long value */

double            dval; /* double value */

zend_refcounted  *counted;

zend_string      *str;

zend_array       *arr;

zend_object      *obj;

zend_resource    *res;

zend_reference   *ref;

zend_ast_ref     *ast;

zval             *zv;

void             *ptr;

zend_class_entry *ce;

zend_function    *func;

struct {

uint32_t w1;

uint32_t w2;

} ww;

} zend_value;


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

雖然PHP屬於弱型別語言,但是在底層實現中還是要區分型別的,因為型別裡有天然的長度,型別引勢記憶體的長度。


底層做了很多型別轉化的處理,讓我們不用關心php的型別和長度,這也是php開發高效的原因之一。


變數知識點:


value、u1、u2都是聯合體,在底層是要區分型別的

u2裡面有個重要的變數next,next會在陣列中解決衝突使用

2.寫時複製(Copy On Write)


struct _zend_string {

zend_refcounted_h gc;

zend_ulong        h;                /* hash value */

size_t            len;

char              val[1];

};

1

2

3

4

5

6

zend_refcounted_h 作用是string型別的引用計數的結構體,h是字串對應的hash值,它後面會用到陣列裡,len代表字串的長度,char是字串的值,因為C言語中字串遇到\0就會自動結束,二進位制是不安全的,所以php加上了長度。


$value1 = 'stark';

$value2 = $value1;

$value2 = 'zcc';

1

2

3

php的寫時複製是這樣發生的,如果把v a l u e 1 賦 值 給 value1賦值給value1賦值給value2,兩個變數指向的是同一個實體記憶體地址,存在硬碟上的某一個塊裡,也許地址是0x7fff5e01c00,當$value2賦值新的值時,zend_refcounted_h引用計數減一,zcc存入新的地址。可以看我之前的文章。


3.字串的引用型別


struct _zend_reference {

zend_refcounted_h gc;

zval              val;

};

1

2

3

4

可以跟著程式碼執行一下,看看你心裡的預期和實際列印出的值是否一致


$a = 'hello';

$b = &$a;

var_dump($a,$b);


$b = 'stark';

var_dump($a,$b);


unset($b);

var_dump($a,$b);

1

2

3

4

5

6

7

8

9

執行結果:


[root@dd2065d03db8 code]# /usr/local/php7.1.0/bin/php refer.php

string(5) "hello"

string(5) "hello"

string(5) "stark"

string(5) "stark"

string(5) "stark"

NULL

1

2

3

4

5

6

7

原始碼中的陣列HashTable


struct _zend_array {

zend_refcounted_h gc;

union {

struct {

ZEND_ENDIAN_LOHI_4(

zend_uchar    flags,

zend_uchar    nApplyCount,

zend_uchar    nIteratorsCount,

zend_uchar    consistency)

} v;

uint32_t flags;

} u;

uint32_t          nTableMask;

Bucket           *arData;

uint32_t          nNumUsed;

uint32_t          nNumOfElements;

uint32_t          nTableSize;

uint32_t          nInternalPointer;

zend_long         nNextFreeElement;

dtor_func_t       pDestructor;

};


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

nTableMask是計算陣列的索引值,*arData儲存陣列裡的key=>value的鍵值對,nNumUsed表示已經使用的空間,nNumOfElements真正的元素個數,nTableSize是arData的大小,nTableSize預設大小是8位元組,記憶體不夠每次擴容都x2,以此類推。


記憶體管理

在malloc申請記憶體時宣告瞭size大小,但是回收時沒有傳size,怎麼做到準確釋放size大小記憶體的呢?


void *ptr=malloc(size);

free(ptr);

1

2

php7記憶體介面


void *ptr=_emalloc(size);

_efree(ptr);

1

2

1.Small記憶體的管理


記憶體的基本概念:chunk、page、各種規格的記憶體。


chunk: 2MB 大小的記憶體

page :4KB大小的記憶體

#define ZEND_MM_CHUNK_SIZE (2 * 1024 * 1024)               /* 2 MB  */

#define ZEND_MM_PAGE_SIZE  (4 * 1024)                      /* 4 KB  */

#define ZEND_MM_PAGES      (ZEND_MM_CHUNK_SIZE / ZEND_MM_PAGE_SIZE)  /* 512 */

1

2

3

記憶體規格


記憶體預分配:使用mmap分配chunk

記憶體分類:


1.Small(30種規格) (size <= 3KB)

2.Large (3KB < size <= 2MB-4KB)

3.Huge(size > 2MB-4KB)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010128/viewspace-2871664/,如需轉載,請註明出處,否則將追究法律責任。

相關文章