总结
简短建议(来自更多数据信息,请参阅答案)
为避免内存泄漏,您可以:
- 当变量变得无用时立即取消设置变量
- 您可以使用 xDebug 按函数详细报告内存消耗并查找内存泄漏
- 您可以设置 memory_limit(例如 5Mb)以避免虚拟内存分配
问题
除了库和变量之外,PHP 可以使用内存的什么?我使用以下代码监视变量及其 ~ 3Mb 使用的内存:
$vars = array_keys(get_defined_vars());
$cnt_vars = count($vars);
$allsize = 0;
for ($j = 0; $j < $cnt_vars; $j++) {
try
{
$size = @serialize($$vars[$j]);
$size = strlen($size);
}
catch(Exception $e){
$str = json_encode($$vars[$j]);
$str = str_replace(array('{"','"}','":"','":'), '', $str);
$size = strlen($str);
}
$vars[$j] = array(
'size' => $size,
'name' => $vars[$j]
);
$allsize += $size;
}
和库需要 ~ 18Mb (libcurl 等)所以总共 21 Mb,但是
PMAP -X (进程) 显示,总内存消耗为 kB:314028 RSS:74704 脏:59672
因此,总实际消耗量为 ~74Mb。我还在我的 pmap 中看到一些带有 [anon] 映射的大块用于使用此块的 PHP 是什么?
PHP 版本: 5.5.9-1Ubuntu4.14PHP 扩展:
root@webdep:~# php -m
[PHP Modules]
bcmath
bz2
calendar
Core
ctype
curl
date
dba
dom
ereg
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
json
libxml
mbstring
mcrypt
mhash
openssl
pcntl
pcre
PDO
pdo_pgsql
pgsql
Phar
posix
readline
Reflection
session
shmop
SimpleXML
soap
sockets
SPL
standard
sysvmsg
sysvsem
sysvshm
tokenizer
wddx
xml
xmlreader
xmlwriter
Zend OPcache
zip
zlib
[Zend Modules]
Zend OPcache
PHP 与编译为单个二进制文件的 C 或 CPP 代码不同。你所有的脚本都在Zend虚拟机中执行。大部分内存由 VM 本身消耗。这包括加载扩展使用的内存、PHP 进程使用的共享库(.so 文件)和任何其他共享资源。
我不记得确切的来源,但我在某处读到,近 70% 的总 CPU 周期被 PHP 内部消耗,只有 30% 用于您的代码(如果我在这里错了,请纠正我)。这与内存消耗没有直接关系,但应该给出一个关于PHP如何工作的想法。
关于匿名块,我在另一个SO答案中找到了一些细节。答案是关于Java的,但同样应该也适用于PHP。
负离子块是通过malloc或mmap分配的"大"块 - 请参阅 手册页。因此,它们与 Java 堆无关(其他 而不是整个堆应该存储在这样一个 块)。
-
这是SO答案的实际链接https://stackoverflow.com/a/1483482/1012809
-
有关匿名内存页(匿名)的更多详细信息,请查看此文章https://techtalk.intersec.com/2013/07/memory-part-2-understanding-process-memory/
-
另请查看此幻灯片共享,了解有关 PHP 内存管理的更多详细信息http://www.slideshare.net/jpauli/understanding-php-memory
我建议禁用一些扩展。这应该为您节省一些未使用的内存。
注意:这不是一个答案,而是OP要求的信息,但注释字段太短了......这些更多的是如何调试此类问题的工具。
Xdebug 的文档非常全面,他们应该通过复制他们的文档来告诉如何使用它比我更好的方法。您提供的脚本有点模糊,所以我没有自己进行跟踪,但它会为您提供内存使用情况的逐行差异。
基本上将xdebug.show_mem_delta
设置为1
启用 Xdebug 以生成函数跟踪,然后您可以在文本编辑器中打开它以查看泄漏内存的确切部分。
然后,您可以比较初始(或中间位置)总内存,看看它与您看到的实际内存使用情况相差多少。
TRACE START [2007-05-06 14:37:26]
0.0003 114112 +114112 -> {main}() ../trace.php:0
这里的总内存将是 114112
.
如果差异真的很大,你可能想使用 shell_exec()
之类的东西来获取所有行之间的实际内存使用情况,并输出它,然后你可以将该输出与 Xdebug 的内存输出进行比较,看看差异发生在哪里。
如果差异来自脚本的第一行,那么罪魁祸首可能是 PHP 的扩展。如果有任何可疑的扩展,请参阅php -m
。
首先创建一个数组,以调查它正在占用的内存
$startMemory = memory_get_usage();
$array = range(1, 100000);
echo memory_get_usage() - $startMemory, ' bytes';
一个整数是8 bytes
(在64 bit unix machine
上使用long
类型),这里100000 integers
,所以你显然需要800000 bytes
。这就像0.76 MB
.
这个数组给出了14649024 bytes
.这是13.97 MB
- 比估计的多十八倍。
以下是所涉及的不同组件的内存使用情况的快速摘要:
| 64 位 |32 位---------------------------------------------------兹瓦尔 | 24 字节 |16 字节+ 循环气相色谱信息 | 8 字节 | 4 字节+ 分配标头 | 16 字节 | 8 字节===================================================兹瓦尔 (价值) 总计 | 48 字节 |28 字节===================================================铲斗 | 72 字节 |36 字节+ 分配标头 | 16 字节 | 8 字节+ 指针 | 8 字节 | 4 字节===================================================存储桶(数组元素)总计 | 96 字节 |48 字节===================================================总计 |144 字节 |76 字节
同样,对于大型静态数组,如果我调用如下:
$startMemory = memory_get_usage();
$array = new SplFixedArray(100000);
for ($i = 0; $i < 100000; ++$i) {
$array[$i] = $i;
}
echo memory_get_usage() - $startMemory, ' bytes';
这将导致5600640 bytes
这是每个元素56 bytes
,因此远低于普通数组使用的每个元素144 bytes
。这是因为固定数组不需要存储桶结构。因此,每个元素只需要一个zval (48 bytes)
和一个pointer (8 bytes)
,从而给出观察到的56 bytes
。
希望这会有所帮助。
你看到的数字没有问题,你不应该组合它们,这只是"三倍",你看到不同的部分(只读、可执行、可写)单独列出的库,你的数字是正确的。