与 PHP 5.6 相比,我在 PHP 7 中面临更多的内存消耗

Posted

技术标签:

【中文标题】与 PHP 5.6 相比,我在 PHP 7 中面临更多的内存消耗【英文标题】:I am facing more memory consumption in Php 7 compare to PHP 5.6 【发布时间】:2017-02-06 00:20:38 【问题描述】:

当我做基准测试时,我发现 php 7 使用的内存比 PHP 5.6 多。

所以,我做了一个测试。我运行的脚本仅包含:

  $a=10;

以下是我在没有任何模块的情况下使用 PHP CLI 时使用的内存的结果 (php -n)

php 5.6 = 222600 Bytes
php 7.0 = 350448 Bytes

* PHP 5.6.23 (cli) (built: Jun 22 2016 12:13:15)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies 

* PHP 7.0.9 (cli) (built: Jul 20 2016 10:47:41) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

环境是

操作系统:窗口 10 服务器:IIS(虽然我使用的是 CLI,而不是服务器),具有快速 cgi 机器:64 位 php-5.6.23-nts-Win32-VC11-x64 php-7.0.9-nts-Win32-VC14-x64

谁能解释我为什么得到这个结果?


附加测试

按照@gordon 的建议使用此代码,

$i=0;
while ($i++ < 100000) ;

php 5.6:227408 字节

php 7.0:386640 字节

我用这段代码确定了内存使用情况:

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();

【问题讨论】:

您是否使用一些实际代码对其进行了基准测试?即使一个或多或少的空脚本多消耗 100k 也不能告诉我们这在“真实”场景中是否属实。知道哪个更有趣.. 也许可以,但是您在此处给出的示例是人为的,而且毫无意义。如果您想知道为什么您的应用程序会消耗更多内存,请将其作为有关应用程序详细信息的问题。分析您的应用程序。问题的表述方式现在暗示 PHP7 一般会占用更多内存。这是一个有害的建议,因为人们会阅读它并将其视为裸币(毕竟,它是在 Stack Overflow 上编写的),而实际上,PHP7 为减少内存使用做了许多改进。例如见nikic.github.io/2014/12/22/… 嗯,不同版本的stem根本不同,更高级的PHP版本体积也更大也就不足为奇了。不仅如此,Zend Engine 版本不同,甚至编译它们的 VC 版本也不同。到目前为止,这里没有什么意外的,问题是,它是否也会根据脚本的大小呈指数增长,或者差异仍然相似?这么小的代码很难确定。 您怎么知道 PHP 7 不仅效率更高,而且使用更多内存,因为它更快且可用?即它试图帮助你。很明显,您没有设置任何内存限制。所以你在抱怨不同 PHP 版本的默认行为? 抱歉,我没有正确阅读您的价值观。您是正确的,这些值小于 1 MB。我怀疑您所看到的只是 PHP 在启动时所具有的“内务管理默认值”。我怀疑当您开始分配大型数组或大量对象时,内存使用会更有意义。即数十万的条目。然后您应该会看到 PHP 7 更“高效”。即相同数据结构的内存更少。 【参考方案1】:

要了解您问题的答案 - 您需要了解 PHP5 和 PHP7 如何分配内存。

PHP5 分配内存“按请求”假设它的 Zend 引擎结构。

PHP7 中,这是在这方面进行的一些优化,因此“按块”分配内存

在启动时会分配大块内存 在应用内分配时,它会分配小块以避免碎片

这种差异极大地提高了性能(因为引擎不需要在每次需要时在运行时分配内存并节省一些碎片时间),但它增加了“非常小的”程序的内存消耗,其大小小于“块大小”。

是的,PHP7 非常节省大型程序的内存。

您可以在下面的图片中查看所有这些差异:

使用基准构建的图形: 1.php

<?php

ini_set('memory_limit', '5G');
$a=range(1,$argv[1]);

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();
echo PHP_EOL;

bench.sh

// Small programs
(for i in $(seq 0 5 5000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m
(for i in $(seq 0 5 5000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m
//Large Programs
(for i in $(seq 0 50 100000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m    
(for i in $(seq 0 50 100000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m

八度抽屉

php7;php7=ans;
php5;php5=ans;
plot(php5(:,5)',[php5(:,1:4)';php7(:,1:4)']');
legend("PHP5 mgu", "PHP5 rmu", "PHP5 rpmu", "PHP5 pmu","PHP7 mgu", "PHP7 rmu", "PHP7 rpmu", "PHP7 pmu");

了解更多

    PHP7/PHP-NG 官方介绍: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view 官方PHP7/PHP-NG内部变更说明: https://wiki.php.net/phpng-int 官方扩展迁移指南: https://wiki.php.net/phpng-upgrading 来自@NikiC 的好文章: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html 和 http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2 PHP5 内部细节:http://www.phpinternalsbook.com/ Badoo PHP5->PHP7 成功案例及详情:https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/

【讨论】:

感谢您的精彩解释,我需要这个。你能分享一些参考资料吗,我可以在哪里查看zend 2和zend 3的内存分配差异,比如php 7启动时分配的最小块大小是多少。 今晚我将附上一些参考资料。 添加了阅读更多链接到 PHP5/PHP7 内部的描述,详细说明了我的答案【参考方案2】:

您的测试显示在 PHP 7.0 中使用了更多内存,因为测试代码非常简单。

众所周知,由于内部 ZEND 引擎(解释器核心)的彻底重写,PHP 7.0 比 PHP 5.6 使用更少的内存(并且速度更快)

正如Gordon 评论的那样,PHP 7.0 中的新功能和改进很可能需要一个“引导程序”,这在对小段代码进行测试时会导致负面结果。

让我们尝试一些更复杂的方法:构建一个包含 10.000 个整数的数组,然后使用 Quicksort 算法对其进行排序。

这是我得到的结果:

PHP 7.0

Memory Usage: 1432752
Real Memory Usage: 4194304
Real Peak Memory Usage: 4194304
Peak Memory Usage: 3152360


PHP 5.6

Memory Usage: 2756744
Real Memory Usage: 4980736
Real Peak Memory Usage: 6029312
Peak Memory Usage: 5710464

而且,一个简单的 20 行快速排序仍然与具有数千行代码、许多类声明、许多实例的实际应用程序相去甚远......

我已经在http://phptester.net上运行了测试

下面是代码

<?php
function quick_sort($array)

    $length = count($array);
    $pivot = $array[0];
    $left = $right = array();
    for($i = 1; $i < count($array); $i++)
    
        if($array[$i] < $pivot)
        
            $left[] = $array[$i];
        
        else
        
            $right[] = $array[$i];
        
    
    return array_merge(quick_sort($left), array($pivot), quick_sort($right));


$unsorted = array();
for($i=0;$i<10000;$i++)

    $unsorted[] = rand(1,1000000);


$sorted = quick_sort($unsorted);

$lf = "<br/>";

echo $lf;
echo "Memory Usage: ".memory_get_usage();
echo $lf;
echo "Real Memory Usage: ".memory_get_usage(true);
echo $lf;
echo "Real Peak Memory Usage: ".memory_get_peak_usage(true);
echo $lf;
echo "Peak Memory Usage: ".memory_get_peak_usage();
echo $lf;

感谢 PHP 中的快速排序算法:http://andrewbaxter.net/quicksort.php

【讨论】:

感谢@Paolo,我完全同意它对于大型程序更有效,如果您分享一些有关 php7 和 php5.6 中的引导的信息,将会非常有帮助。它真的会帮助我更好地理解 php。【参考方案3】:

预先我想说的是,如果您在实际代码中看到 PHP 7 中报告的内存使用量较高,最可能的原因是 PHP 7 会将 mysqlnd 缓冲查询的内存使用情况报告为内存使用情况的一部分。在 PHP 5 中没有报告这个内存使用情况(当然内存仍然被使用)。对于大型查询,这可能会产生很大的不同。

现在来看你的实际情况,这基本上是请求启动后 PHP 的内存使用情况。 answer by MobDev 已经解释了为什么“实际”内存使用量存在差异,这是内存使用量指标,它报告 PHP 的分配器从内核的系统分配器请求了多少内存。正如 MobDev 指出的那样,PHP 7 将以更大的块 (2MB) 分配内存,并且在缓存分配的块方面也更加积极。

但是,这并不能解释“非真实”内存使用的差异,它没有考虑这些分配器的详细信息。使用内存分析器很容易检查内存是否准确,例如通过USE_ZEND_ALLOC=0 valgrind --tool=massif 运行 PHP。 USE_ZEND_ALLOC=0 部分指示 PHP 不使用自己的分配器。

首先,这将告诉您实际内存使用情况和 PHP 报告的内存使用情况有很大差异。 Massif 将显示 PHP 5.6 的 3.2MB 使用量和 PHP 7 的 2.3MB 使用量。原因是 PHP 只报告通过它自己的分配器 (ZMM) 的内存,而在多个请求中存活的许多结构没有使用它分配。

通过系统分配器的最大分配(因此未在内存使用中报告)是:

                       | PHP 5.6 | PHP 7
interned string buffer | 1 MB    | 150 KB + strings
GC buffer              | 320 KB  | 320 KB
internal classes/funcs | >1.3 MB | >0.5 MB

“内部类/函数”数字是一个粗略的下限,因为这里涉及到许多难以计数的小分配。一个主要区别是可见的,那就是 PHP 7 不使用固定的内部字符串缓冲区(列出的大小是我看到的哈希表缓冲区的大小,不包括字符串本身的大小)。

但是,这仍然没有回答实际报告的内存使用情况的问题。在这种情况下,最大的分配是:

             | PHP 5.6 | PHP 7
VM stack     | 130 KB  | 256 KB
Object store | 64 KB   | (8 KB)
CG arena     | ---     | 64 KB

这里有几个不同之处。主要的一点是 PHP 7 使用了更大的 VM 页面大小(大约是原来的两倍)。此外,PHP 7 使用一个 arena 来存储某些结构(如用户函数),它以 64KB 的默认大小开始。另一方面,在 PHP 7 中对象存储缓冲区的大小要小得多。

所以基本上 TL;DR 的答案是 PHP 7 使用更大的 VM 堆栈页面大小。

【讨论】:

【参考方案4】:

与 Php 7.0 相比,Php 5.6 需要的字节更少。

【讨论】:

我认为 PHP7 的内存效率更高? 是的@halfer PHP7效率更高,如果你从中到大规模使用,如果你存储100000个值,从1-100000的数组中做一些操作,你会发现主要区别.

以上是关于与 PHP 5.6 相比,我在 PHP 7 中面临更多的内存消耗的主要内容,如果未能解决你的问题,请参考以下文章

在 xampp 中从 5.6 迁移 PHP 7 时,PDO 驱动程序在 Windows 中没有价值

Mac Pro 实现 PHP-5.6 与 PHP-7.0 等多版本切换

php5.5.6如何配置

LAMP-安装PHP 5/7

php7新特性

markdown 如何在PHP 7.1和PHP 5.6之间的Laravel Valet中切换PHP版本