Home >Backend Development >PHP Tutorial >关于php内存释放问题(二)
今天抽了一上午时间,来看了看之前解决过内存问题的代码,相对来说,我对自己代码的优化程序非常不满意,一次性导入四万条数据就使代码变得如此繁琐,我想这不是根本的解决方法。通过网上检索,对问题有进一步的分析:
在php内存溢出的问题中,前两种方法就不提了(可以参考《关于php内存释放问题》在博园中),不难分析出,其实问题的关键在于如何在循环中同步释放内存,而不是循环几百条来释放一次,因为大家在调试中会发现,如果我们把循环中复用性较高的代码封装到函数中,然后使用调用子函数的形式,会是程序的执行速度降低,大约几十倍左右,这个下降幅度会随着数据量的不同而改变。
循环嵌套的形式,在处理较大数据量的数组,有很多使用 unset($a),的形式来及时释放内存,但是 实际上这么做是没有意义的,引用以下分析:
”在引擎中,变量名和它们的值实际上是两个不同的概念。值本身是一个无名的zval*存储体(在本例中,是一个字符串值),它被通过zend_hash_add()赋给变量$a。如果两个变量名都指向同一个值,会发生什么呢?
{
zval *helloval;
MAKE_STD_ZVAL(helloval);
ZVAL_STRING(helloval, "Hello World", 1);
zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),&helloval, sizeof(zval*), NULL);
zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval, sizeof(zval*), NULL);
}
此时,你可以实际地观察$a或$b,并且会看到它们都包含字符串"Hello World"。遗憾的是,接下来,你继续执行第三行代码"unset($a);"。此时,unset()并不知道$a变量指向的数据还被另一个变量所使用,因此它只是盲目地释放掉该内存。任何随后的对变量$b的存取都将被分析为已经释放的内存空间并因此导致引擎崩溃。
这个问题可以借助于zval(它有好几种形式)的第四个成员refcount加以解决。当一个变量被首次创建并赋值时,它的refcount被初始化为1,因为它被假定仅由最初创建它时相应的变量所使用。当你的代码片断开始把helloval赋给$b时,它需要把refcount的值增加为2;这样以来,现在该值被两个变量所引用:
{
zval *helloval;
MAKE_STD_ZVAL(helloval);
ZVAL_STRING(helloval, "Hello World", 1);
zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),&helloval, sizeof(zval*), NULL);
ZVAL_ADDREF(helloval);
zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval,sizeof(zval*),NULL);
}
现在,当unset()删除原变量的$a相应的副本时,它就能够从refcount参数中看到,还有另外其他人对该数据感兴趣;因此,它应该只是减少refcount的计数值,然后不再管它。“
综上,我们最需要做的其实是减少最初存储数据的数组,上面的例子,在循环中及时释放掉数组中已经处理完的元素,这样随着循环,内存占用量的会一直上下波动(内存回收机制问题),但不会一直增长,也就达到了我们最初的目的。当然,一次性最大的数据处理量还是取决于服务器给php分配的内存,单次数据量读入数组就超出了限制,哪就是神仙也没有办法了,哈哈