于任何一个ZVAL容器存储的变量,分配了一个zval结构,这个结构确保其和以zval变量分配的内存的开始对齐, 从而在zval_gc_info类型指针的强制转换时,其可以作为zval使用。在zval字段后面有一个联合体:u。 u包括gc_root_buffer结构的buffered字段和zval_gc_info结构的next字段。 这两个字段一个是表示垃圾收集机制缓存的根结点,一个是zval_gc_info列表的下一个结点, 垃圾收集机制缓存的结点无论是作为根结点,还是列表结点,都可以在这里体现。 ALLOC_ZVAL在分配了内存后会调用GC_ZVAL_INIT用来初始化替代了zval的zval_gc_info, 它会把zval_gc_info中的成员u的buffered字段设置成NULL,此字段仅在将其放入垃圾回收缓冲区时才会有值,否则会一直是NULL。 由于PHP中所有的变量都是以zval变量的形式存在,这里以zval_gc_info替换zval,从而成功实现垃圾收集机制在原有系统中的集成。
PHP的垃圾回收机制在PHP5.3中默认为开启,但是我们可以通过配置文件直接设置为禁用,其对应的配置字段为:zend.enable_gc。 在php.ini文件中默认是没有这个字段的,如果我们需要禁用此功能,则在php.ini中添加zend.enable_gc=0或zend.enable_gc=off。 除了修改php.ini配置zend.enable_gc,也可以通过调用gc_enable()/gc_disable()函数来打开/关闭垃圾回收机制。 这些函数的调用效果与修改配置项来打开或关闭垃圾回收机制的效果是一样的。 除了这两个函数PHP提供了gc_collect_cycles()函数可以在根缓冲区还没满时强制执行周期回收。 与垃圾回收机制是否开启在PHP源码中有一些相关的操作和字段。在zend.c文件中有如下代码:
复制代码 代码如下:
static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */
{
OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
if (GC_G(gc_enabled)) {
gc_init(TSRMLS_C);
}
return SUCCESS;
}
/* }}} */
ZEND_INI_BEGIN()
ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
#ifdef ZEND_MULTIBYTE
STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals)
#endif
ZEND_INI_END()
zend.enable_gc对应的操作函数为ZEND_INI_MH(OnUpdateGCEnabled),如果开启了垃圾回收机制, 即GC_G(gc_enabled)为真,则会调用gc_init函数执行垃圾回收机制的初始化操作。 gc_init函数在zend/zend_gc.c 121行,此函数会判断是否开启垃圾回收机制, 如果开启,则初始化整个机制,即直接调用malloc给整个缓存列表分配10000个gc_root_buffer内存空间。 这里的10000是硬编码在代码中的,以宏GC_ROOT_BUFFER_MAX_ENTRIES存在,如果需要修改这个值,则需要修改源码,重新编译PHP。 gc_init函数在预分配内存后调用gc_reset函数重置整个机制用到的一些全局变量,如设置gc运行的次数统计(gc_runs)和gc中垃圾的个数(collected)为0, 设置双向链表头结点的上一个结点和下一个结点指向自己等。除了这种提的一些用于垃圾回收机制的全局变量,还有其它一些使用较多的变量,部分说明如下:
复制代码 代码如下:
typedef struct _zend_gc_globals {
zend_bool gc_enabled; /* 是否开启垃圾收集机制 */
zend_bool gc_active; /* 是否正在进行 */
gc_root_buffer *buf; /* 预分配的缓冲区数组,默认为10000(preallocated arrays of buffers) */
gc_root_buffer roots; /* 列表的根结点(list of possible roots of cycles) */
gc_root_buffer *unused; /* 没有使用过的缓