未显式取消设置时,对象内存使用量过大


exessive object memory usage when not explicitly unsetting

我的一个同事写了一个耗尽可用内存的脚本。我将其缩小到以下基本测试用例:

for ( $i = 0; $i <= 20; $i ++ ) {
    echo memory_get_usage(). '<br />';
    $Survey = new Survey( 14 );
    echo memory_get_usage(). '<br /><br />';
}
exit('done');

在第三次迭代时中断:

3116696
49123440
49123440
95518368
95518368
[E_ERROR] Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes)

我设法解决了这个问题,通过简单地在循环中添加一个unset()调用:

for ( $i = 0; $i <= 20; $i ++ ) {
    echo memory_get_usage(). '<br />';
    $Survey = new Survey( 14 );
    unset( $Survey );
    echo memory_get_usage(). '<br /><br />';
}
exit('done');

现在,脚本顺利地进行了20次迭代,内存使用相对稳定:

3116816
49123488
49123488
50691656
50691656
51088912
51088912
51079064
51079064
50535368
50535368
50809296
50809296
51033392
51033392
51157208
51157208
50543856
50543856
50892760
50892760
51045160
51045160
51132688
51132688
50535968
50535968
50968632
50968632
51058080
51058080
51143304
51143304
50562136
50562136
51067432
51067432
51067768
51067768
51170824
51170824
50551712
done

这让我很困惑!难道垃圾收集器不应该清理对象,因为它的变量已经被覆盖了吗?我正在运行PHP 5.3,所以循环引用不可能是这个问题的原因。

在5.3中循环引用仍然是一个问题:

<

清理问题/strong>

尽管在任何作用域中不再有符号指向this结构,它不能被清除,因为数组元素"1"仍然存在指向同一个数组。因为没有外在的符号指向它,用户没有办法清理这个结构;这样就会出现内存泄漏。幸运的是,PHP将清理这些数据结构,但在此之前,这是取占用内存中的宝贵空间。这种情况经常发生,如果你实现解析算法或其他有子节点的东西指向"父"元素。同样的情况也可能发生当然,在物体上,它实际上更有可能发生,比如对象总是隐式地被引用使用。

Survey中可能还有一些占用内存的资源,占用了所有这些内存;观察到的行为应该是一个循环和这样一个资源的组合。

Survey到底是什么?

这个问题是由循环引用和每个对象占用1/3可用内存的组合引起的。将代码更改为:

for ( $i = 0; $i <= 20; $i ++ ) {
    echo memory_get_usage(). '<br />';
    gc_collect_cycles();
    echo memory_get_usage(). '<br />';
    $Survey = new Survey( 14 );
    echo memory_get_usage(). '<br /><br />';
}
exit('done');

给我:

3116456
3116680
49123288
49123288
49123288
95518160
95518160
50452344
96236360
96236360
50365776
96261312
96261312
50477296
96348608
96348608
50453072
96349752
96349752
50478440
96364872
96364872
50468192
96365240
96365240
50478808
96370760
96370760
50473712
96366072
96366072
50474120
96371448
96371448
50479088
96375352
96375352
50478024
96376672
96376672
50480408
96374984
96374984
50476336
96373032
96373032
50478456
96372216
96372216
50475520
96371288
96371288
50477528
96378824
96378824
50483056
96383992
96383992
50482696
96376592
96376592
50475656
96378072
done

您可以清楚地看到,手动启动垃圾收集器释放了被"孤立"对象占用的内存。我认为问题在于没有足够的时间让垃圾收集器启动,因为对象太大了。

目前最简单的解决方案是添加unset()调用——从长远来看,我将研究使Survey对象更节省内存的方法。

感谢Jon和CodeCaster为我指明了正确的方向!