未显式取消设置时对象内存使用过多

发布于 2024-12-11 23:33:46 字数 1264 浏览 0 评论 0原文

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

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,因此循环引用不可能是导致此问题的原因。

A colleague of mine wrote a script that was exhausting the available memory. I narrowed it down to the following basic test case:

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

This breaks on the third iteration:

3116696
49123440

49123440
95518368

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

I managed to fix this, by simply adding an unset() call in the loop:

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');

Now the script goes through its 20 iterations nice and smooth, with a relatively constant memory usage:

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

This confuses me! Isn't the garbage collector supposed to clean up the object, as it's variable has been overwritten? I'm running PHP 5.3, so circular references can't be the cause of this problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱要勇敢去追 2024-12-18 23:33:46

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

清理问题

尽管在任何作用域中不再有符号指向此
结构体,它无法被清理,因为数组元素“1”仍然
指向同一个数组。因为没有外部符号
指向它,用户没有办法清理这个结构;
因此你会遇到内存泄漏。幸运的是,PHP 会清理这些数据
请求末尾的结构,但在此之前,这正在采取
节省内存中宝贵的空间。这种情况经常发生,如果你
在你有孩子的情况下实现解析算法或其他东西
指向“父”元素。同样的情况也可能发生
当然,对于物体,它实际上更有可能发生,例如
对象始终通过引用隐式使用。

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

Survey 到底包含什么内容?

Circular references can still be a problem in 5.3:

Cleanup Problems

Although there is no longer a symbol in any scope pointing to this
structure, it cannot be cleaned up because the array element "1" still
points to this same array. Because there is no external symbol
pointing to it, there is no way for a user to clean up this structure;
thus you get a memory leak. Fortunately, PHP will clean up this data
structure at the end of the request, but before then, this is taking
up valuable space in memory. This situation happens often if you're
implementing parsing algorithms or other things where you have a child
point back at a "parent" element. The same situation can also happen
with objects of course, where it actually is more likely to occur, as
objects are always implicitly used by reference.

There is probably also some memory-hogging resource inside Survey that takes up all of this memory; the observed behavior should be a combination of a ref cycle and such a resource.

What's in Survey exactly?

草莓味的萝莉 2024-12-18 23:33:46

该问题是由循环引用和每个对象占用 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 为我指明了正确的方向!

The problem was caused by the combination of circular references and the objects taking up 1/3rd of the available memory each. Changing the code to this:

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');

showed me:

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

You can clearly see that firing up the garbage collector manually frees up the memory taken up by the 'orphaned' object. I think the problem was there just was not enough time for the garbage collector to kick in, as the objects were so big.

The easiest solution for now is adding the unset() call - for the long term though I will be investigating ways to make the Survey object more memory-efficient.

Thanks to Jon and CodeCaster for pointing me in the right direction!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文