php 为什么这个简单的php脚本会泄漏内存?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1145775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does this simple php script leak memory?
提问by mjgoins
In hopes of trying to avoid future memory leaks in php programs (drupal modules, etc.) I've been messing around with simple php scripts that leak memory.
为了避免将来在 php 程序(drupal 模块等)中出现内存泄漏,我一直在处理泄漏内存的简单 php 脚本。
Could a php expert help me find what about this script causes the memory usage to continually climb?
php 专家能帮我找出这个脚本导致内存使用量不断攀升的原因吗?
Try running it yourself, changing various parameters. The results are interesting. Here it is:
尝试自己运行它,更改各种参数。结果很有趣。这里是:
<?php
function memstat() {
print "current memory usage: ". memory_get_usage() . "\n";
}
function waste_lots_of_memory($iters) {
$i = 0;
$object = new StdClass;
for (;$i < $iters; $i++) {
$object->{"member_" . $i} = array("blah blah blha" => 12345);
$object->{"membersonly_" . $i} = new StdClass;
$object->{"onlymember"} = array("blah blah blha" => 12345);
}
unset($object);
}
function waste_a_little_less_memory($iters) {
$i = 0;
$object = new StdClass;
for (;$i < $iters; $i++) {
$object->{"member_" . $i} = array("blah blah blha" => 12345);
$object->{"membersonly_" . $i} = new StdClass;
$object->{"onlymember"} = array("blah blah blha" => 12345);
unset($object->{"membersonly_". $i});
unset($object->{"member_" . $i});
unset($object->{"onlymember"});
}
unset($object);
}
memstat();
waste_a_little_less_memory(1000000);
memstat();
waste_lots_of_memory(10000);
memstat();
For me, the output is:
对我来说,输出是:
current memory usage: 73308
current memory usage: 74996
current memory usage: 506676
[edited to unset more object members]
[编辑以取消设置更多对象成员]
回答by Andrew Moore
unset()doesn't free the memory used by a variable. The memory is freed when the "garbage collector" (in quotes since PHP didn't have a real garbage collector before version 5.3.0, just a memory free routine which worked mostly on primitives) sees fit.
unset()不会释放变量使用的内存。当“垃圾收集器”(在引号中,因为 PHP 在 5.3.0 版本之前没有真正的垃圾收集器,只是一个主要用于原语的无内存例程)认为合适时释放内存。
Also, technically, you shouldn't need to call unset()since the $objectvariable is limited to the scope of your function.
此外,从技术上讲,您不需要调用,unset()因为$object变量仅限于您的函数范围。
Here is a script to demonstrate the difference. I modified your memstat()function to show the memory difference since the last call.
这是一个演示差异的脚本。我修改了您的memstat()函数以显示自上次调用以来的内存差异。
<?php
function memdiff() {
static $int = null;
$current = memory_get_usage();
if ($int === null) {
$int = $current;
} else {
print ($current - $int) . "\n";
$int = $current;
}
}
function object_no_unset($iters) {
$i = 0;
$object = new StdClass;
for (;$i < $iters; $i++) {
$object->{"member_" . $i}= array("blah blah blha" => 12345);
$object->{"membersonly_" . $i}= new StdClass;
$object->{"onlymember"}= array("blah blah blha" => 12345);
}
}
function object_parent_unset($iters) {
$i = 0;
$object = new StdClass;
for (;$i < $iters; $i++) {
$object->{"member_" . $i}= array("blah blah blha" => 12345);
$object->{"membersonly_" . $i}= new StdClass;
$object->{"onlymember"}= array("blah blah blha" => 12345);
}
unset ($object);
}
function object_item_unset($iters) {
$i = 0;
$object = new StdClass;
for (;$i < $iters; $i++) {
$object->{"member_" . $i}= array("blah blah blha" => 12345);
$object->{"membersonly_" . $i}= new StdClass;
$object->{"onlymember"}= array("blah blah blha" => 12345);
unset ($object->{"membersonly_" . $i});
unset ($object->{"member_" . $i});
unset ($object->{"onlymember"});
}
unset ($object);
}
function array_no_unset($iters) {
$i = 0;
$object = array();
for (;$i < $iters; $i++) {
$object["member_" . $i] = array("blah blah blha" => 12345);
$object["membersonly_" . $i] = new StdClass;
$object["onlymember"] = array("blah blah blha" => 12345);
}
}
function array_parent_unset($iters) {
$i = 0;
$object = array();
for (;$i < $iters; $i++) {
$object["member_" . $i] = array("blah blah blha" => 12345);
$object["membersonly_" . $i] = new StdClass;
$object["onlymember"] = array("blah blah blha" => 12345);
}
unset ($object);
}
function array_item_unset($iters) {
$i = 0;
$object = array();
for (;$i < $iters; $i++) {
$object["member_" . $i] = array("blah blah blha" => 12345);
$object["membersonly_" . $i] = new StdClass;
$object["onlymember"] = array("blah blah blha" => 12345);
unset ($object["membersonly_" . $i]);
unset ($object["member_" . $i]);
unset ($object["onlymember"]);
}
unset ($object);
}
$iterations = 100000;
memdiff(); // Get initial memory usage
object_item_unset ($iterations);
memdiff();
object_parent_unset ($iterations);
memdiff();
object_no_unset ($iterations);
memdiff();
array_item_unset ($iterations);
memdiff();
array_parent_unset ($iterations);
memdiff();
array_no_unset ($iterations);
memdiff();
?>
If you are using objects, make sure the classes implements __unset()in order to allow unset()to properly clear resources. Try to avoid as much as possible the use of variable structure classes such as stdClassor assigning values to members which are not located in your class template as memory assigned to those are usually not cleared properly.
如果您正在使用对象,请确保类实现__unset()以允许unset()正确清除资源。尽量避免使用可变结构类,例如stdClass或为不在类模板中的成员赋值,因为分配给这些类的内存通常不会被正确清除。
PHP 5.3.0 and up has a better garbage collector but it is disabled by default. To enable it, you must call gc_enable()once.
PHP 5.3.0 及更高版本具有更好的垃圾收集器,但默认情况下已禁用。要启用它,您必须调用gc_enable()一次。
回答by Frank Farmer
memory_get_usage()"Returns the amount of memory, in bytes, that's currently being allocated to your PHP script."
memory_get_usage()"返回当前分配给 PHP 脚本的内存量,以字节为单位。"
That's the amount of memory allocated to the process by the OS, notthe amount of memory used by assigned variables. PHP does not always release memory back to the OS -- but that memory can still be re-used when new variables are allocated.
这是操作系统分配给进程的内存量,而不是分配的变量使用的内存量。PHP 并不总是将内存释放回操作系统——但在分配新变量时仍可以重新使用该内存。
Demonstrating this is simple. Change the end of your script to:
证明这一点很简单。将脚本的结尾更改为:
memstat();
waste_lots_of_memory(10000);
memstat();
waste_lots_of_memory(10000);
memstat();
Now, if you're correct, and PHP is actually leaking memory, you should see memory useage grow twice. However, here's the actual result:
现在,如果您是对的,并且 PHP 实际上正在泄漏内存,您应该会看到内存使用量增长了两倍。然而,这是实际结果:
current memory usage: 88272
current memory usage: 955792
current memory usage: 955808
This is because memory "freed" after the initial invocation of waste_lots_of_memory() is re-used by the second invocation.
这是因为在初始调用waste_lots_of_memory() 后“释放”的内存被第二次调用重新使用。
In my 5 years with PHP, I've written scripts that have processed millions of objects and gigabytes of data over a period of hours, and scripts that have run for months at a time. PHP's memory management isn't great, but it's not nearly as bad as you're making it out to be.
在我使用 PHP 的 5 年中,我编写了在几个小时内处理数百万个对象和千兆字节数据的脚本,以及一次运行数月的脚本。PHP 的内存管理不是很好,但也没有你想象的那么糟糕。
回答by troelskn
memory_get_usagereports how much memory php has allocated from the os. It doesn't necessarily match the size of all variables in use. If php has a peak use of memory, it may decide not to return the unused amount of memory right away. In your example, the function waste_a_little_less_memoryunsets unused variables over time. So the peak usage is relatively small. The waste_lots_of_memorybuilds up a lot of variables (=lots of used memory) before deallocating it. So the peak usage is much larger.
memory_get_usage报告 php 从操作系统分配了多少内存。它不一定与正在使用的所有变量的大小相匹配。如果 php 内存使用高峰,它可能决定不立即返回未使用的内存量。在您的示例中,该函数会waste_a_little_less_memory随着时间的推移取消设置未使用的变量。所以高峰使用量比较小。在waste_lots_of_memory重新分配前积聚了大量的变量(=大量使用的内存)。所以峰值使用量要大得多。
回答by marr75
My understanding of memory_get_usage() is that it's output can depend on a wide range of operating system and version factors.
我对 memory_get_usage() 的理解是它的输出可能取决于广泛的操作系统和版本因素。
More importantly, unsetting a variable does not instantly free it's memory, deallocate it from the process, and give it back to the operating system (again, characteristics of this operation are operating system dependent).
更重要的是,取消设置变量不会立即释放它的内存,从进程中释放它,并将其返回给操作系统(同样,此操作的特性取决于操作系统)。
In short, you probably need a more complicated setup to look at memory leaks.
简而言之,您可能需要更复杂的设置来查看内存泄漏。
回答by Jason
I'm not sure about the exact workings of it in PHP, but in some other languages an object containing other objects, when set to null, does not inherently set the other objects to null. It terminates the reference to those objects, but as PHP does not have "garbage collection" in a Java sense, the sub-objects exist in memory until they are removed individually.
我不确定它在 PHP 中的确切工作原理,但在其他一些语言中,包含其他对象的对象在设置为 null 时不会固有地将其他对象设置为 null。它终止对这些对象的引用,但由于 PHP 没有 Java 意义上的“垃圾收集”,因此子对象存在于内存中,直到它们被单独删除。
回答by Miguel
memory_get_usage() does not returns the immediate memory usage, but stored memory to run the process. IN the case of a huge array unset($array_a) will not release memory but consume more according to the memory_get_usage() in my system...
memory_get_usage() 不返回即时内存使用情况,而是返回运行进程的存储内存。在巨大数组 unset($array_a) 的情况下,不会释放内存,但会根据系统中的 memory_get_usage() 消耗更多......
$array_a="(SOME big array)";
$array_b="";
//copy array_a to array_b
for($line=0; $line<100;$line++){
$array_b[$line]=$array_a[$line];
}
unset($array_a); //having this shows actually a bigger consume
print_r($array_b);
echo memory_get_usage();
echo memory_get_usage();

