PHP7.4中FFI的介紹(代碼示例)

2019-11-01     科技i關注

本篇文章給大家帶來的內容是關於php底層原理的垃圾回收機制的介紹(代碼示例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

php垃圾回收機制,對於PHPer來說是一個不陌生但是又不是很熟悉的內容。那麼php是怎麼實現對不需要的內存進行回收的呢?

php變量的內部存儲結構

首先還是需要了解下基礎知識,便於垃圾回收原理內容的理解。大家都知道php是由C編寫而成的,所以php變量的內部存儲結構也會和C語言相關,即zval的結構體:

struct _zval_struct {

union {

long lval;

double dval;

struct {

char *val;

int len;

} str;

HashTable *ht;

zend_object_value obj;

zend_ast *ast;

} value; //變量value值

zend_uint refcount__gc; //引用計數內存中使用次數,為0刪除該變量

zend_uchar type; //變量類型

zend_uchar is_ref__gc; //區分是否是引用變量

};

從上面結構體內容可以看出每一個php變量都會由變量類型、value值、引用計數次數和是否是引用變量四部分組成

註:上面zval結構體是php5.3版本之後的結構,php5.3之前因為沒有引入新的垃圾回收機制,即GC,所以命名也沒有_gc;而php7版本之後由於性能問題所以改寫了zval結構,這裡不再表述

引用計數原理

了解了php變量的內部存儲結構之後,我們再了解下php變量賦值相關的原理和早期垃圾回收機制

變量容器

非array和object變量

每次將常量賦值給一個變量時,都會產生一個變量容器

舉例:

$a = '許錚的技術成長之路';

xdebug_debug_zval('a')

結果:

a: (refcount=1, is_ref=0)='許錚的技術成長之路'

array和object變量

會產生元素個數+1的變量容器

舉例:

$b = [

'name' => '許錚的技術成長之路',

'number' => 3

];

xdebug_debug_zval('b')

結果:

b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='許錚的技術成長之路', 'number' => (refcount=1, is_ref=0)=3)

賦值原理(寫時複製技術)

了解了常量賦值之後,接下來我們從內存角度思考變量之間的賦值

舉例:

$a = [

'name' => '許錚的技術成長之路',

'number' => 3

]; //創建一個變量容器,變量a指向給變量容器,a的ref_count為1

$b = $a; //變量b也指向變量a指向的變量容器,a和b的ref_count為2

xdebug_debug_zval('a', 'b');

$b['name'] = '許錚的技術成長之路1';//變量b的其中一個元素髮生改變,此時會複製出一個新的變量容器,變量b重新指向新的變量容器,a和b的ref_count變成1

xdebug_debug_zval('a', 'b');

結果:

a: (refcount=2, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='許錚的技術成長之路', 'number' => (refcount=1, is_ref=0)=3)

b: (refcount=2, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='許錚的技術成長之路', 'number' => (refcount=1, is_ref=0)=3)

a: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='許錚的技術成長之路', 'number' => (refcount=1, is_ref=0)=3)

b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='許錚的技術成長之路1', 'number' => (refcount=1, is_ref=0)=3)

所以,當變量a賦值給變量b的時候,並沒有立刻生成一個新的變量容器,而是將變量b指向了變量a指向的變量容器,即內存"共享";而當變量b其中一個元素髮生改變時,才會真正發生變量容器複製,這就是寫時複製技術

引用計數清0

當變量容器的ref_count計數清0時,表示該變量容器就會被銷毀,實現了內存回收,這也是php5.3版本之前的垃圾回收機制

舉例:

$a = "許錚的技術成長之路";

$b = $a;

xdebug_debug_zval('a');

unset($b);

xdebug_debug_zval('a');

結果:

a: (refcount=2, is_ref=0)='許錚的技術成長之路'

a: (refcount=1, is_ref=0)='許錚的技術成長之路'

循環引用引發的內存泄露問題

但是php5.3版本之前的垃圾回收機制存在一個漏洞,即當數組或對象內部子元素引用其父元素,而此時如果發生了刪除其父元素的情況,此變量容器並不會被刪除,因為其子元素還在指向該變量容器,但是由於所有作用域內都沒有指向該變量容器的符號,所以無法被清除,因此會發生內存泄漏,直到該腳本執行結束

舉例:

$a = array( 'one' );

$a[] = &$a;

xdebug_debug_zval( 'a' );

由於該示例不好輸出結果,用圖表示,如圖:

舉例:

unset($a);

xdebug_debug_zval('a');

如圖:

新的垃圾回收機制

php5.3版本之後引入根緩衝機制,即php啟動時默認設置指定zval數量的根緩衝區(默認是10000),當php發現有存在循環引用的zval時,就會把其投入到根緩衝區,當根緩衝區達到配置文件中的指定數量(默認是10000)後,就會進行垃圾回收,以此解決循環引用導致的內存泄漏問題

確認為垃圾的準則

1、如果引用計數減少到零,所在變量容器將被清除(free),不屬於垃圾

2、如果一個zval 的引用計數減少後還大於0,那麼它會進入垃圾周期。其次,在一個垃圾周期中,通過檢查引用計數是否減1,並且檢查哪些變量容器的引用次數是零,來發現哪部分是垃圾。

總結

垃圾回收機制:

1、以php的引用計數機制為基礎(php5.3以前只有該機制)

2、同時使用根緩衝區機制,當php發現有存在循環引用的zval時,就會把其投入到根緩衝區,當根緩衝區達到配置文件中的指定數量後,就會進行垃圾回收,以此解決循環引用導致的內存泄漏問題(php5.3開始引入該機制)

以上就是php底層原理的垃圾回收機制的介紹(代碼示例)的詳細內容,更多請關注其它相關文章!

更多技巧請《轉發 + 關注》哦!

文章來源: https://twgreatdaily.com/zh-tw/dTHlI24BMH2_cNUgmmek.html