运行这个简单的脚本时,我得到下面发布的输出。 这让我觉得我的代码或Zend Framework / Magento堆栈中都存在内存泄漏。迭代任何类型的Magento集合时会发生此问题。 我有什么遗漏或做错了吗?
脚本:
$customersCollection = Mage::getModel('customer/customer')->getCollection();
foreach($customersCollection as $customer) {
$customer->load();
$customer = null;
echo memory_get_usage(). "\n";
}
输出:
102389104
102392920
...
110542528
110544744
答案 0 :(得分:7)
您的问题是,当您可以通过集合查询加载必要的数据时,每次迭代都会发出相当昂贵的查询:
$collection = Mage::getResourceModel('customer/customer_collection')->addAttributeToSelect('*');
将执行相同操作,但只能在一个查询中执行。这种方法的警告是,如果customer_load_before
或customer_load_after
事件有任何自定义事件观察者(这些事件没有核心观察者),则需要为每个数据模型手动运行观察者。
编辑:信用osonodoar以发现不正确的类引用(客户/客户与客户/客户集合)
答案 1 :(得分:3)
对象(或其他值)的内存只能在PHP进程中的任何位置都没有引用时释放。在您的情况下,行$customer = null
仅将对该对象的引用数减少一个,但它不会使其达到零。
如果你考虑一个更简单的循环,这可能会变得更清晰:
$test = array('a' => 'hello');
foreach ( $test as $key => $value )
{
// $value points at the same memory location as $test['a']
// internally, that "zval" has a "refcount" of 2
$value = null;
// $value now points to a new memory location, but $test['a'] is unnaffected
// the refcount drops to 1, but no memory is freed
}
因为你正在使用对象,所以有一个额外的转折 - 你可以修改循环内的对象而不创建它的新副本:
$test = array('a' => new __stdClass);
// $test['a'] is an empty object
foreach ( $test as $key => $value )
{
// $value points at the same object as $test['a']
// internally, that object has a "refcount" of 2
$value->foo = "Some data that wasn't there before";
// $value is still the same object as $test['a'], but that object now has extra data
// This requires additional memory to store that object
$value = null;
// $value now points to a new memory location, but $test['a'] is unnaffected
// the refcount drops to 1, but no memory is freed
}
// $test['a']->foo now contains the string assigned in the loop, consuming extra memory
在您的情况下,->load()
方法可能会依次扩大$customersCollection
每个成员中的数据量,每个成员需要更多内存。在循环之前和之后检查$customersCollection
可能会证实这一点。
答案 2 :(得分:0)
首先,当取消设置变量使用unset($ variable)而不是$ variable = null时。它基本上是一样的,但是你的意图更清楚。
其次,PHP意味着死亡 - 内存泄漏不是一个大问题,因为PHP请求可能持续几秒钟,然后进程终止并且它正在使用的所有内存都被释放用于下一个请求。除非您遇到扩展问题,否则无需担心。
编辑:这并不是说不要担心代码的质量,但对于这样的事情,除非造成问题,否则最有可能不值得尝试阻止它发生。
答案 3 :(得分:0)
处理内存泄漏的其他方法是在循环中调用exec并让exec函数执行导致内存泄漏的作业部分。
因此,一旦它完成其部分并终止该exec内的所有内存泄漏将被释放。
因此,在大量迭代的情况下,这种记忆损失会继续增加,否则将会非常谨慎。
答案 4 :(得分:0)
@benmarks响应在这里是正确的方法,因为在循环中调用load()是一个非常昂贵的调用。
调用$ customer-> load()将逐步分配$ customersCollection引用的内存,该内存在循环结束前不会被释放。
但是,如果因任何原因需要调用load(),则下面的代码不会泄漏内存,因为GC会在每次迭代中释放模型分配的所有内存。
$customersCollection = Mage::getModel('customer/customer')->getCollection();
foreach($customersCollection as $customer) {
$customerCopy = Mage::getModel('customer/customer')->load($customer->getId());
//Call to $customerCopy methods
echo memory_get_usage(). "\n";
}