zoukankan      html  css  js  c++  java
  • php 垃圾处理机制

    在php5.3版本之前, php变量的回收机制只是简单的通过计数来处理(当refcount=0时,会回收内存),但这样会出现一个问题

    $a=array("str");

    $a[]=&$a;

    unset($a);

    执行unset之前,$a的refcount 为2,执行unset之后,$a的refcout为1,因为是1不等于0,不能被回收内存,即为垃圾,当然,在php脚本执行完毕后,所分配的内存将全部被回收,但是现在php除了应用于脚本以外,更多的地方用于写守护服务(当然我不推荐),可能长达一个月,两个月才结束脚本,这期间例如上面的程序会产生内存溢出

     注:unset并不能释放内存,需要看zval的refcount是否为0

    php5.3以后增了GC垃圾回收机制

    在分配zval时,以zval_gc_info为单位

    复制代码
    #define ALLOC_ZVAL(z)                                     
        do {                                                
            (z) = (zval*)emalloc(sizeof(zval_gc_info));        
            GC_ZVAL_INIT(z);                                
        } while (0)
    
    typedef struct _zval_gc_info {
        zval z;
        union {
            gc_root_buffer       *buffered;
            struct _zval_gc_info *next;
        } u;
    } zval_gc_info;
    
    #define FREE_ZVAL(z)                                     
        do {                                                
            GC_REMOVE_ZVAL_FROM_BUFFER(z);                    
            efree(z);                                        
        } while (0)
    复制代码
    复制代码
    typedef struct _gc_root_buffer {
        struct _gc_root_buffer   *prev;        /* double-linked list               */
        struct _gc_root_buffer   *next;
        zend_object_handle        handle;    /* must be 0 for zval               */
        union {
            zval                 *pz;
            zend_object_handlers *handlers;
        } u;
    } gc_root_buffer;
    复制代码

    php的GC回收机制启动时,会分配10000个gc_root_buffer的空间

    复制代码
    ZEND_API void gc_init(TSRMLS_D)  
    {  
        if (GC_G(buf) == NULL && GC_G(gc_enabled)) {  
            GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);  
            GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];  
            gc_reset(TSRMLS_C);  
        }  
    }  
    复制代码

    在unset($a)时,详见这里 在active_systom_table找到key为a的zval后,对其value执行析构函数,将其zval的refcount-1,若减1后的值为0,说明可能直接释放内存,若为大于0,放到gc_root_buffer中

    复制代码
    ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */  
    {   
        Z_DELREF_PP(zval_ptr);  
        if (Z_REFCOUNT_PP(zval_ptr) == 0) {  
            TSRMLS_FETCH();  
      
            if (*zval_ptr != &EG(uninitialized_zval)) {  
                GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);  
                zval_dtor(*zval_ptr);  
                efree_rel(*zval_ptr);  
            }  
        } else {  
            TSRMLS_FETCH();  
      
            if (Z_REFCOUNT_PP(zval_ptr) == 1) {  
                Z_UNSET_ISREF_PP(zval_ptr);  
            }  
      
            GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);  
        }  
    }  
    复制代码
    复制代码
    #define GC_ZVAL_CHECK_POSSIBLE_ROOT(z) /
        gc_zval_check_possible_root((z) TSRMLS_CC)
    
    static zend_always_inline void gc_zval_check_possible_root(zval *z TSRMLS_DC)
    {
        if (z->type == IS_ARRAY || z->type == IS_OBJECT) {
            gc_zval_possible_root(z TSRMLS_CC);
        }
    }
    
    ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
    {
        if (UNEXPECTED(GC_G(free_list) != NULL &&
                       GC_ZVAL_ADDRESS(zv) != NULL &&
                       GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
                       (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
                        GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
            /* The given zval is a garbage that is going to be deleted by
             * currently running GC */
            return;
        }
    
        if (zv->type == IS_OBJECT) {
            GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
            return;
        }
    
        GC_BENCH_INC(zval_possible_root);
        //如果zv中的gc_root_buffer最后两位不是紫色,则进行处理
        if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
            GC_ZVAL_SET_PURPLE(zv); //设置为紫色
    
            if (!GC_ZVAL_ADDRESS(zv)) {
                gc_root_buffer *newRoot = GC_G(unused);
    
                if (newRoot) {
                    GC_G(unused) = newRoot->prev;
                } else if (GC_G(first_unused) != GC_G(last_unused)) {
                    newRoot = GC_G(first_unused);
                    GC_G(first_unused)++;
                } else {
                    if (!GC_G(gc_enabled)) {
                        GC_ZVAL_SET_BLACK(zv);
                        return;
                    }
                    zv->refcount__gc++;
                    gc_collect_cycles(TSRMLS_C);
                    zv->refcount__gc--;
                    newRoot = GC_G(unused);
                    if (!newRoot) {
                        return;
                    }
                    GC_ZVAL_SET_PURPLE(zv);
                    GC_G(unused) = newRoot->prev;
                }
    
                newRoot->next = GC_G(roots).next;
                newRoot->prev = &GC_G(roots);
                GC_G(roots).next->prev = newRoot;
                GC_G(roots).next = newRoot;
    
                GC_ZVAL_SET_ADDRESS(zv, newRoot); //将gc_root_buffer放到zval_gc_info结构体中
    
                newRoot->handle = 0;
                newRoot->u.pz = zv;
    
                GC_BENCH_INC(zval_buffered);
                GC_BENCH_INC(root_buf_length);
                GC_BENCH_PEAK(root_buf_peak, root_buf_length);
            }
        }
    }
    复制代码

    将当前zval放到gc_root_bufer中,每个zval只放一次,依据是该zval所在的zval_gc_info中的gc_root_buffer的颜色 是否是 紫色

    #define GC_ZVAL_GET_COLOR(v)
    GC_GET_COLOR(((zval_gc_info*)(v))->u.buffered)

    #define GC_GET_COLOR(v)
    (((zend_uintptr_t)(v)) & GC_COLOR)

    若不是紫色,则设置为紫色

    #define GC_ZVAL_SET_PURPLE(v)
    GC_SET_PURPLE(((zval_gc_info*)(v))->u.buffered)

    #define GC_SET_PURPLE(v)
    (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) | GC_PURPLE))

    宏GC_ZVAL_SET_ADDRESS(zv, newRoot);用来将gc_root_buffer newRoot 放到zv相应位置

    复制代码
    #define GC_COLOR  0x03
    
    #define GC_BLACK  0x00
    #define GC_WHITE  0x01
    #define GC_GREY   0x02
    #define GC_PURPLE 0x03
    
    #define GC_ZVAL_SET_ADDRESS(v, a) 
        GC_SET_ADDRESS(((zval_gc_info*)(v))->u.buffered, (a))
    
    #define GC_SET_ADDRESS(v, a) 
        (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a))))
    复制代码
    GC_ZVAL_SET_ADDRESS宏中先将v强制转为zval_gc_info类型,本身在为zval分配内存时, 就是以zval_gc_info为单位的,将强转为zval*,因为只需要对zval结构体填充数据,不需要 gc_root_buffer *bufer这个样成员
    因为在结构体zval_gc_val中,zval z是第一个成员,那么z的地址也是zval_gc_info本身的内存地址

    在PHP GC中,使用颜色来标明垃圾的处理过程

    指针无论在32位机或64位机,最后两位均为0,

    gc_collect_cycles处理垃圾

    
    


    复制代码
    ZEND_API int gc_collect_cycles(TSRMLS_D)
    {
        int count = 0;
    
        if (GC_G(roots).next != &GC_G(roots)) {
            zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
    
            if (GC_G(gc_active)) {
                return 0;
            }
            GC_G(gc_runs)++;
            GC_G(zval_to_free) = FREE_LIST_END;
            GC_G(gc_active) = 1;
            gc_mark_roots(TSRMLS_C);
            gc_scan_roots(TSRMLS_C);
            gc_collect_roots(TSRMLS_C);
    
            orig_free_list = GC_G(free_list);
            orig_next_to_free = GC_G(next_to_free);
            p = GC_G(free_list) = GC_G(zval_to_free);
            GC_G(zval_to_free) = NULL;
            GC_G(gc_active) = 0;
    
            /* First call destructors */
            while (p != FREE_LIST_END) {
                if (Z_TYPE(p->z) == IS_OBJECT) {
                    if (EG(objects_store).object_buckets &&
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
                        !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
    
                          EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
                          EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
                          EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.object, Z_OBJ_HANDLE(p->z) TSRMLS_CC);
                          EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
                      }
                }
                count++;
                p = p->u.next;
            }
    
            /* Destroy zvals */
            p = GC_G(free_list);
            while (p != FREE_LIST_END) {
                GC_G(next_to_free) = p->u.next;
                if (Z_TYPE(p->z) == IS_OBJECT) {
                    if (EG(objects_store).object_buckets &&
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
                        EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
                        Z_TYPE(p->z) = IS_NULL;
                        zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
                    }
                } else if (Z_TYPE(p->z) == IS_ARRAY) {
                    Z_TYPE(p->z) = IS_NULL;
                    zend_hash_destroy(Z_ARRVAL(p->z));
                    FREE_HASHTABLE(Z_ARRVAL(p->z));
                } else {
                    zval_dtor(&p->z);
                    Z_TYPE(p->z) = IS_NULL;
                }
                p = GC_G(next_to_free);
            }
    
            /* Free zvals */
            p = GC_G(free_list);
            while (p != FREE_LIST_END) {
                q = p->u.next;
                FREE_ZVAL_EX(&p->z);
                p = q;
            }
            GC_G(collected) += count;
            GC_G(free_list) = orig_free_list;
            GC_G(next_to_free) = orig_next_to_free;
        }
    
        return count;
    }
    复制代码
     
    gc_mark_roots(TSRMLS_C); 为垃圾打下颜色标记,遍历gc_root_buffer,其中的u.pz将紫色变更为灰色,遍历u.pz该array zval的每个元素,将其refcount-1,
    gc_collect_roots遍历gc_root_buffer,如果refcount==0再设置为白色,表示为垃圾,若refcount >0,表示别人在使用,设置为黑色
    
    
    复制代码
    static void gc_mark_roots(TSRMLS_D)
    {
        gc_root_buffer *current = GC_G(roots).next;
    
        while (current != &GC_G(roots)) {
            if (current->handle) {
                if (EG(objects_store).object_buckets) {
                    //处理对象,暂时不用看
                }
            } else {
                if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
                    zval_mark_grey(current->u.pz TSRMLS_CC);
                } else {
                    GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
                    GC_REMOVE_FROM_BUFFER(current);
                }
            }
            current = current->next;
        }
    }
    复制代码
    
    
    
     
    复制代码
    static void zval_mark_grey(zval *pz TSRMLS_DC)
    {
        Bucket *p;
    
    tail_call:
        if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
            p = NULL;
            GC_BENCH_INC(zval_marked_grey);
            GC_ZVAL_SET_COLOR(pz, GC_GREY);
    
            if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
                //对象的处理, 暂时不用管
            } else if (Z_TYPE_P(pz) == IS_ARRAY) {
                if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
                    GC_ZVAL_SET_BLACK(pz);
                } else {
                    p = Z_ARRVAL_P(pz)->pListHead;
                }
            }
            while (p != NULL) {
                pz = *(zval**)p->pData;
                if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
                    pz->refcount__gc--;
                }
                if (p->pListNext == NULL) {
                    goto tail_call;
                } else {
                    zval_mark_grey(pz TSRMLS_CC);
                }
                p = p->pListNext;
            }
        }
    }
    复制代码

     第二次遍历gc_root_buffer,如果zv中的颜色为灰色,且refcount=0,再置为白色,若refcount>0,置为黑色(不是垃圾)

    复制代码
    static void gc_scan_roots(TSRMLS_D)
    {
        gc_root_buffer *current = GC_G(roots).next;
    
        while (current != &GC_G(roots)) {
            if (current->handle) {
                zval z;
    
                INIT_PZVAL(&z);
                Z_OBJ_HANDLE(z) = current->handle;
                Z_OBJ_HT(z) = current->u.handlers;
                zobj_scan(&z TSRMLS_CC);
            } else {
                zval_scan(current->u.pz TSRMLS_CC);
            }
            current = current->next;
        }
    }
    复制代码
    复制代码
    static int zval_scan(zval *pz TSRMLS_DC)
    {
        Bucket *p;
    
    tail_call:    
        if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
            p = NULL;
            if (pz->refcount__gc > 0) {
                zval_scan_black(pz TSRMLS_CC);
            } else {
                GC_ZVAL_SET_COLOR(pz, GC_WHITE);
                if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
                   //处理object
                } else if (Z_TYPE_P(pz) == IS_ARRAY) {
                    if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
                        GC_ZVAL_SET_BLACK(pz);
                    } else {
                        p = Z_ARRVAL_P(pz)->pListHead;
                    }
                }
            }
            while (p != NULL) {
                if (p->pListNext == NULL) {
                    pz = *(zval**)p->pData;
                    goto tail_call;
                } else {
                    zval_scan(*(zval**)p->pData TSRMLS_CC);
                }
                p = p->pListNext;
            }
        }
        return 0;
    }
    复制代码

    遍历gc_root_buffer链表,将zv颜色为白色的数据放置单独一个链表,全部回收

    复制代码
    static void gc_collect_roots(TSRMLS_D)
    {
        gc_root_buffer *current = GC_G(roots).next;
    
        while (current != &GC_G(roots)) {
            if (current->handle) {
                if (EG(objects_store).object_buckets) {
                    struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
                    zval z;
    
                    GC_SET_ADDRESS(obj->buffered, NULL);
                    INIT_PZVAL(&z);
                    Z_OBJ_HANDLE(z) = current->handle;
                    Z_OBJ_HT(z) = current->u.handlers;
                    zobj_collect_white(&z TSRMLS_CC);
                }
            } else {
                GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
                zval_collect_white(current->u.pz TSRMLS_CC);
            }
    
            GC_REMOVE_FROM_BUFFER(current);
            current = current->next;
        }
    }
    复制代码
    复制代码
    static void zval_collect_white(zval *pz TSRMLS_DC)
    {
        Bucket *p;
    
    tail_call:
        if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
            p = NULL;
            GC_ZVAL_SET_BLACK(pz);
    
            if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
                ...
            } else {
                if (Z_TYPE_P(pz) == IS_ARRAY) {
                    p = Z_ARRVAL_P(pz)->pListHead;
                }
            }
    
            /* restore refcount and put into list to free */
            pz->refcount__gc++;
            ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
            GC_G(zval_to_free) = (zval_gc_info*)pz;
    
            while (p != NULL) {
                pz = *(zval**)p->pData;
                if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
                    pz->refcount__gc++;
                }
                if (p->pListNext == NULL) {
                    goto tail_call;
                } else {
                    zval_collect_white(pz TSRMLS_CC);
                }
                p = p->pListNext;
            }
        }
    }
    复制代码
  • 相关阅读:
    svn command line tag
    MDbg.exe(.NET Framework 命令行调试程序)
    Microsoft Web Deployment Tool
    sql server CI
    VS 2010 One Click Deployment Issue “Application Validation did not succeed. Unable to continue”
    mshtml
    大厂程序员站错队被架空,只拿着五折工资!苟活和离职,如何选择?
    揭秘!Windows 为什么会蓝屏?微软程序员竟说是这个原因...
    喂!千万别忘了这个C语言知识!(~0 == -1 问题)
    Linux 比 Windows 更好,谁反对?我有13个赞成理由
  • 原文地址:https://www.cnblogs.com/yangjinqiang/p/11129308.html
Copyright © 2011-2022 走看看