PHP中的zend_string_release和zend_string_release_ex

PHP中,释放zend_string可以使用zend_string_release或者zend_string_release_ex。那什么时候应该用zend_string_release,什么时候应该用zend_string_release_ex呢?一句话总结就是,如果我们不确定这个zend_string是不是persistent方式分配的内存,那么就用zend_string_release,如果我们确定这个zend_string是不是以persistent方式分配的内存,那么就使用zend_string_release_ex,因为zend_string_release_ex可以稍微提升性能。我们来看一下这两个函数。

首先是zend_string_release

1
2
3
4
5
6
7
8
9
10
#define pefree(ptr, persistent)  ((persistent)?free(ptr):efree(ptr))

static zend_always_inline void zend_string_release(zend_string *s)
{
if (!ZSTR_IS_INTERNED(s)) {
if (GC_DELREF(s) == 0) {
pefree(s, GC_FLAGS(s) & IS_STR_PERSISTENT);
}
}
}

这个函数做的事情比较简单,先对zend_string的引用计数减一,如果引用计数变为了0,那么就会真正的去调用pefree释放内存。除此之外,这里还需要判断zend_string的分配方式,如果是persistent方式分配的,那么调用free,否则调用efree

我们再来看看zend_string_release_ex

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static zend_always_inline void zend_string_release_ex(zend_string *s, int persistent)
{
if (!ZSTR_IS_INTERNED(s)) {
if (GC_DELREF(s) == 0) {
if (persistent) {
ZEND_ASSERT(GC_FLAGS(s) & IS_STR_PERSISTENT);
free(s);
} else {
ZEND_ASSERT(!(GC_FLAGS(s) & IS_STR_PERSISTENT));
efree(s);
}
}
}
}

他做的事情也比较简单,先对zend_string的引用计数减一,如果引用计数变为了0,那么判断persistent再决定调用哪一个free。乍眼一看,似乎zend_string_release_ex做的事情比zend_string_release还多,多了一个断言,为啥性能就会更好呢?因为这个ZEND_ASSERTPHP的非debug模式下,是不会执行的。并且,如果对persistent进行硬编码,编译器会对zend_string_release_ex进行优化:

1
2
3
4
5
6
7
if (persistent) {
ZEND_ASSERT(GC_FLAGS(s) & IS_STR_PERSISTENT);
free(s);
} else {
ZEND_ASSERT(!(GC_FLAGS(s) & IS_STR_PERSISTENT));
efree(s);
}

也就是说,在编译的时候,就已经知道要走哪一个分支了。