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

dev*_*rCK 19 php php-internals

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

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

  $a=10;
Run Code Online (Sandbox Code Playgroud)

以下是我在没有任何模块的情况下使用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
Run Code Online (Sandbox Code Playgroud)

环境是

  • 操作系统:窗口10
  • 服务器:IIS(尽管我使用的是CLI,而不是服务器),具有快速cgi
  • 机器:64位
  • PHP-23年6月5日 - NTS-Win32的VC11-64
  • PHP-7.0.9-NTS-Win32的VCl 4-64

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


额外的测试

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

$i=0;
while ($i++ < 100000) ;
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

Mob*_*Dev 22

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

PHP5 通过它的Zend引擎结构来分配内存"按请求".

PHP7这方面做了一些优化,所以内存分配"由块"

  • 在开始时它分配大块内存
  • 在应用内分配中,它分配小块以避免碎片

这种差异使得性能得到了非常好的提升(因为引擎不需要在每次运行时都在运行时分配内存并节省碎片时间),但它会增加"非常小"程序的内存消耗,这些程序的大小低于"块大小".

是的,PHP7在大型程序上非常节省内存.

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

用于大型程序的PHP内存分配 用于小程序的PHP内存分配

使用基准构建的图形: 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;
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

八度抽屉

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");
Run Code Online (Sandbox Code Playgroud)

阅读更多

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


Pao*_*olo 13

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

由于内部ZEND引擎(解释器核心)的彻底重写,已知PHP 7.0使用的内存更少(并且更快)

正如Gordon评论的那样,很可能PHP 7.0中的新功能和改进需要一个"引导程序",当在小块代码上进行测试时会导致负面结果.

让我们尝试一些更复杂的东西:构建一个10.000整数的数组,然后使用Quicksort algorythm 对它进行排序.

这是我得到的结果:

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
Run Code Online (Sandbox Code Playgroud)

还有一个简单的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;
Run Code Online (Sandbox Code Playgroud)

感谢PHP中的quicksort algorythm:http://andrewbaxter.net/quicksort.php


Nik*_*kiC 9

前期我想说,如果你在实际代码中看到PHP 7报告的内存使用量更高,最可能的原因是PHP 7会报告mysqlnd缓冲查询的内存使用情况,作为内存使用的一部分.在PHP 5中,未报告此内存使用情况(但当然仍使用内存).对于大型查询,这可以产生非常大的差异.

现在根据您的实际情况,这基本上是请求启动后立即使用PHP的内存使用情况.MobDev答案已经解释了为什么"实际"内存使用存在差异,这是内存使用量指标,它报告PHP的分配器从内核的系统分配器请求了多少内存.正如MobDev所指出的那样,PHP 7将以更大的块(2MB)分配内存,并且对缓存已分配的块也更加积极.

但是,这并不能解释"非实际"内存使用中的差异,而这种内存使用并未将这些分配器详细信息考虑在内.通过使用内存分析器,例如通过运行PHP,很容易检查内存是否正常运行USE_ZEND_ALLOC=0 valgrind --tool=massif.该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
Run Code Online (Sandbox Code Playgroud)

"内部类/功能"数字是粗略的下限,因为这里涉及许多小分配很难计算.一个主要区别是可见的,即PHP 7不使用固定的实习字符串缓冲区(列出的大小是我看到的哈希表缓冲区,它不包括字符串本身的大小).

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

             | PHP 5.6 | PHP 7
VM stack     | 130 KB  | 256 KB
Object store | 64 KB   | (8 KB)
CG arena     | ---     | 64 KB
Run Code Online (Sandbox Code Playgroud)

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

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