zend內(nèi)存管理

c和php的最主要區(qū)別:是否控制內(nèi)存指針。

內(nèi)存管理

? ? 在php內(nèi)核層,每次都做到及時釋放,這是相當難的事情。php語言內(nèi)核細節(jié)太多,還有版本兼容等等,寫擴展的我們,其實只需要用php提供的工具去做就好了,沒必要讀源碼里的各個實現(xiàn)細節(jié),實在費力不討好。

因此,在php內(nèi)核里申請和釋放內(nèi)存,不使用c語言的malloc這些函數(shù),而是使用php提供的宏。void *emalloc()

? ? ? ?有種情況:當php執(zhí)行時候,遇到了錯誤,就會die掉,此時的實現(xiàn),應該類似longjmp,跳到設置好的地址,估計就是goto。這種情況,會導致:遇到錯誤時候,直接跳出,后面的釋放內(nèi)存的步驟沒有執(zhí)行,就導致內(nèi)存泄露了。 所以,PHP有一個zendMM引擎,扮演這類似OS的作用,如果我們使用zendMM提供的宏申請內(nèi)存,如果出現(xiàn)問題,內(nèi)存會被主動回收。?

有時候,我們需要一些持久內(nèi)存,請求結束也不被回收,這時候,可以使用傳統(tǒng)的malloc等函數(shù)進行內(nèi)存分配。但是有種特殊情況,比如運行之后,根據(jù)程序的邏輯條件,判斷,才知道是否要持久分配。 所以呀,zendMM有對應的宏:(Zend/zend_alloc.h)

#define pemalloc(size, persistent) ?((persistent)?malloc(size): emalloc(size))

第二個參數(shù)是0和1,表示是否持久分配。

void *malloc(size_t count); void *emalloc(size_t count);

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *pemalloc(size_t count, char persistent);

void *calloc(size_t count); void *ecalloc(size_t count);? void *pecalloc(size_t count, char persistent);

void *realloc(void *ptr, size_tcount);? void *erealloc(void *ptr, size_t count);? void *perealloc(void *ptr, size_t count, char persistent);

void *strdup(void *ptr); void *estrdup(void *ptr);? void *pestrdup(void *ptr, char persistent);

void free(void *ptr); void efree(void *ptr); void pefree(void *ptr, char persistent);

pefree也是需要持久化參數(shù)的。如果對非永久內(nèi)存使用free,會導致雙倍釋放,在持久內(nèi)存上使用efree,會導致段錯誤。所以,就不要用malloc分配,只用emalloc,和pemalloc。釋放的時候,代碼要記住,內(nèi)存是不是持久非配,然后選擇對應的efree或者pefree(這里有點歧義,2本書上說的不一樣,等弄明白,坐實驗才知道了)

還有一種是safe_emalloc*的宏。這種比可以防止內(nèi)存溢出。


引用計數(shù)

平時多少也了解了些,php底層的變量是共享的,賦值是引用。

$a = "hello world";

$b = $a;

unset($a);

$a賦值給$b,php有做優(yōu)化,不是簡單的copy。 而是讓b也指向"hello world",這時候引用計數(shù)是2,有2個變量引用他。unset時候,只減少引用計數(shù),只有當引用計數(shù)為0時候,才真正的釋放內(nèi)存。

有了引用計數(shù)的優(yōu)化,還需要一些策略來應對特殊情況:

$a = 1;

$b = $a;

$b += 5;

第二行,b和a同時引用1,第三行,a進行了+=操作,如果此時把a和b指向的值1進行和操作,那么會導致b出現(xiàn)錯誤。b和a都會變成6,。所以,這時候有一種情況叫寫時復制

當進行寫操作時候,如果引用計數(shù)<2,說明只有一個變量在引用。 ?否則,說明有多個變量共享,此時,b要進行寫,應該拷貝一份值,獨立出去,這時候,a和b是分別引用的不同的值,此時b就可以進行+=操作了。

C語言實現(xiàn):

zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC)

{// 參數(shù)是b

zval **varval, *varcopy;//注意一個是1級指針,一個是2級

if (zend_hash_find(EG(active_symbol_table),

varname, varname_len + 1, (void**)&varval) == FAILURE) {//查找到的變量地址由varval存著

/* 變量不存在 */

return NULL;

}

if ((*varval)->refcount < 2) { //后面還會介紹一種情況

/* 變量名只有一個引用, 不需要隔離 */

return *varval;

}

/* 其他情況, 對zval *做一次淺拷貝 */

MAKE_STD_ZVAL(varcopy);//淺拷貝就是分配一段空間


varcopy = *varval;//

/* 對zval *進行一次深拷貝 */

zval_copy_ctor(varcopy);//深拷貝就是把結構給復制了,復制varcpy指向的地址

/* 破壞varname和varval之間的關系, 這一步會將varval的引用計數(shù)減小1 */

zend_hash_del(EG(active_symbol_table), varname, varname_len + 1);

/* 初始化新創(chuàng)建的值的引用計數(shù), 并為新創(chuàng)建的值和varname建立關聯(lián) */

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 = varcopy,與a是獨立的空間,此時,在進行b+=5,就可以互不干擾了


$a ?= 1;

$b = &$a;

$b += 5;

這時候呢,在php語法里,b和a都是相同地址的引用,應該是a和b指向相同的地址,并且值變成了6。那么zend引擎如果實現(xiàn)呢,其實和上面寫時復制的原理一毛一樣,但是在if那多一個判斷,如果是引用,那么就不復制隔離。zval結構除了refcount,還有一個is_ref(是否有引用),

if((*varval)->is_ref ?|| (*varval)->ref_count <2){

? ? ? ? return *varval;

}

這兩種情況,分別是寫時復制機制和寫時修改機制,任何一個變量,只能使用一種機制??紤]一種情況:

$a = 1;

$b = $a;

$c = &$a;

當$b = ?$a時候,是寫時復制機制

$c = &$a,這時候是寫時修改,由于a本身已經(jīng)是寫時復制機制了,在進行寫時修改,會讓內(nèi)核參數(shù)歧義,因此。這種情況下,內(nèi)核會copy出一份獨立的空間,a和b是寫時修改,b寫時復制


還有一個灰常重要的一點,當一個變量傳遞到函數(shù)里面時候呢,ref_count 一定>=2,一份是自己,還有一個是傳遞給函數(shù)的拷貝。(php里,如果傳參數(shù)到函數(shù)里面,是局部變量,既然是局部變量,說明函數(shù)本身拷貝了一份,因此,這一步的實現(xiàn),是由寫時完成的),后面有接收參數(shù)的方法,那里會用上。也就是說,接收參數(shù)時候,如果需要對這個參數(shù)進行修改,那么要記得在代碼里寫一份寫時復制機制的代碼。內(nèi)核提供了一個修飾符,"/",他會幫我們實現(xiàn)這個寫時復制。



最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • 內(nèi)存是計算機非常關鍵的部件之一,是暫時存儲程序以及數(shù)據(jù)的空間,CPU只有有限的寄存器可以用于 存儲計算數(shù)據(jù),而大部...
    dreamer_lk閱讀 1,633評論 2 10
  • C語言中內(nèi)存分配 在任何程序設計環(huán)境及語言中,內(nèi)存管理都十分重要。在目前的計算機系統(tǒng)或嵌入式系統(tǒng)中,內(nèi)存資源仍然是...
    一生信仰閱讀 1,313評論 0 2
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,197評論 1 10
  • 在這部分中,我覺得是以N 的饑餓感的變化為時間順序來寫的。最初,“the doorbell rang .i was...
    sunnyangle閱讀 215評論 0 0

友情鏈接更多精彩內(nèi)容