【PHP】認識PHP的垃圾回收機制(1)

前言

本文的目的不僅僅是總結PHP的垃圾回收機制,而是要發散思維,從垃圾回收機制中得到更多的細節。

什麼是垃圾回收

垃圾回收機制廣泛存在於各種編程語言中,由於每個變量被定義都會佔用一部分內存空間,如果這些空間長期不被釋放,系統的內存就會出現不足的情況,爲了保證系統能在軟件運行過程中有足夠的內存使用,就需要這樣一套回收機制,釋放掉不需要使用的內存空間。

高級語言一般在自動實現了垃圾回收,不需要我們主動去控制。

PHP的垃圾回收實現

php5.3的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;
     zend_uint refcount__gc;
     zend_uchar type;
     zend_uchar is_ref__gc;
};

其中的refcount__gcis_ref__gc負責垃圾回收機制的計數,這裏被官方成爲"引用計數".

php7的zval

struct _zval_struct {
     union {
          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;
     } value;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
    } u2;
};

zend_refcounted用來實現垃圾回收。

從PHP7開始, 對於在zval的value字段中能保存下的值, 就不再對他們進行引用計數了, 而是在拷貝的時候直接賦值, 這樣就省掉了大量的引用計數相關的操作, 這部分類型有:

  • IS_LONG
  • IS_DOUBLE

對於那種根本沒有值, 只有類型的類型, 也不需要引用計數了:

  • IS_NULL
  • IS_FALSE
  • IS_TRUE
    而對於複雜類型, 一個size_t保存不下的, 那麼我們就用value來保存一個指針, 這個指針指向這個具體的值, 引用計數也隨之作用於這個值上, 而不在是作用於zval上了.
    在這裏插入圖片描述
    IS_ARRAY的結構如下
struct _zend_array {
    zend_refcounted_h gc;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    flags,
                zend_uchar    nApplyCount,
                zend_uchar    nIteratorsCount,
                zend_uchar    reserve)
        } 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;
};

其中zend_refcounted_h保存了垃圾回收相關的結構

typedef struct _zend_refcounted_h {
    uint32_t         refcount;          /* reference counter 32-bit */
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,    /* used for strings & objects */
                uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
        } v;
        uint32_t type_info;
    } u;
} zend_refcounted_h;

這樣,PHP7的垃圾回收是通過refcount來控制的,也是引用計數。

官方文檔

官方文檔的記錄還是php5.2到5.3的垃圾回收機制,基本原理不變,但是控制的變量名稱變了。

當引用計數變成0,系統認爲變量沒有用處,就會被垃圾回收。

在這裏插入圖片描述

參考資料

  • https://www.laruence.com/2018/04/08/3170.html
  • https://www.php.net/manual/zh/features.gc.refcounting-basics.php
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章