1. 程式人生 > >PHP 內存管理 寫時復制 垃圾回收

PHP 內存管理 寫時復制 垃圾回收

分離 賦值 這就是 循環 效率 父子進程共享 說明 原理 算法

PHP中的變量是不需要手動釋放的,內核幫我們實現了變量的內存管理,包括內存的分配和回收

變量深拷貝帶來的問題就是效率和內存浪費嚴重。

解決深拷貝:1、引用計數 2、寫時復制

PHP變量的內存管理就是基於這兩點實現的

當變量賦值、引用的時候不是進行深拷貝,而是多個變量共用一個value,引用計數來記錄這個變量被引用過多少次,當其中的一個變量發生變化時將無法與其他的變量共用

value時,這個時候就需要進行深拷貝進行分離value,這就是寫時復制。

引用計數:

用來記錄當前有多少個zval指向同一個zend_value,當有新的zval指向這個value時,計數器+1,當zval銷毀時,計數書-1,當計數器為0,value就釋放來。

PHP7的引用計數保存在了zend_value中,成員gc用來保存引用計數的。PHP中的局部變量zavl分配在zend_execute_data結構上,也就是我們調試的execute_ex函數中的

execute_data變量,它是運行期間最重要,最關鍵的一個結構,用於局部變量的分配、保存執行位置、調用上下文切換等。

寫時復制:

它只有在有必要的時候即發生寫的時候才進行深拷貝,可以很好的提高效率,例如Linux操作系統中fork子進程時並不會立即復制父進程的地址空間,而是讓父子進程共享一個內存空間,只有在需要寫入的時候才會復制地址空間,從而使各個進程擁有各自獨立的地址空間,也就是說資源的復制是在需要寫入的時候才會進行,在此之前,以只讀方式共享。也並不是所有的類型都可以進行復制,對象資源就無法進行復制。

自動GC回收:

在zval斷開value的執行是如果發現refounct=0則會直接釋放value,這就是變量的回收時機,發生斷開的兩種常用情況為修改變量與函數返回時,修改變量是會斷開value的指向,函數返回時會釋放所有的變量。使用unset()函數也可以主動銷毀一個變量

垃圾回收:

通過引用計數實現了變量的自動GC機制,但是有一種情況是這個機制無法解決的,從而因變量無法回收導致內存始終得不到釋放,造成內存泄漏。這種情況就是循環引用,循環引用就是引用了自身導致無法釋放,比如數組中元素引用了這個數組。因為這種因為循環引用而導致無法釋放的變量稱之為垃圾,PHP引用了另一種機制來對這些垃圾進行回收,也就是垃圾回收器:

這裏要明確,如果一個變量value的refcount減少到0,value釋放,不屬於垃圾。如果一個變量的refcount減少之後大於0,那這可能是垃圾,垃圾回收器會把這種變量收集起來,垃圾回收器會把這種垃圾保存到一個buffer緩存區,等達到一定數量後就會啟動垃圾鑒定回收程序,回收算法原理其實很簡單,既然垃圾回收是因為引用自身引起的啊,那我就把value的refcount減1,如果返回是0就說明是垃圾,否則再把減1恢復,說明是正常變量。

PHP 內存管理 寫時復制 垃圾回收