[代码性能] -- 内存峰值

要点速读

新增变量或有新的运算时一般都会累加内存峰值,但unset变量后不会降低峰值,再新增变量,会利用一些旧变量空出的空间,所以峰值会增加得不明显


当一个进程启动后,进程可以向系统申请分配内存,比如接下来一个变量要占16B容量的话,就要申请足够的容量来装这个变量了

所以PHP在运行过程中根据程序计算的需要,不断地按需求情况向系统申请更多的内存容量,我们先看看下面这样的代码大概用了多少内存:

$a = [
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
	'11111111111111111111111111111111111111',
];
echo memory_get_peak_usage(); //这个函数可以获取内存峰值

我这里运行结果是输出了120216,表示直到调用memory_get_peak_usage函数的时候为止,操作系统一共为PHP进程分配了120216B内存,大概就是117KB

你的运行结果和我的不一定是一样的,看我们的环境具体情况,但误差不会很大,总不会是我得出117KB,而你你就得出2MB这样嘛


unset销毁变量后,内存峰值并不会下降

有些小有经验的程序员都觉得:我积极地去unset变量应该就能节省内存提高性能了

但你可能想错了,看看这个代码实验:

echo memory_get_peak_usage() . PHP_EOL; //120984

$b = [
	new stdClass(),
	new stdClass(),
	new stdClass(),
	get_declared_classes(),
	get_declared_classes(),
	get_declared_classes(),
	get_declared_classes(),
	get_declared_classes(),
	get_declared_classes(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
	get_defined_vars(),
];
echo memory_get_peak_usage() . PHP_EOL; //215576  新增个大数组后的峰值

unset($b);
echo memory_get_peak_usage(); //215600  都unset了变量,为什么峰值还是这么高?

对,为什么unset了还是那么高?继续往下


内存循环利用

unset一个变量后,PHP会将这个变量腾出的空间存到自己的“私房钱”里而不是交还给系统,此时系统一直认为“这个PHP进程就是用了很多内存”

如果再有新变量什么的,PHP就先看看“私房钱”能顶用多少,顶多少算多少,不够用再向系统申请新的内存

$a = [
	$_SERVER
];
echo memory_get_peak_usage() . PHP_EOL; //132320

$b = [
	new stdClass(),
	new stdClass(),
	new stdClass(),
	get_declared_classes(),
	get_defined_vars(),
];
echo memory_get_peak_usage() . PHP_EOL; //147048

//接下来如果新增$c会导致增加对吧!?
unset($a, $b);
echo memory_get_peak_usage() . PHP_EOL; //147048 销毁了峰值也不会下降
$c = [
	[
		$_SERVER,
	]
];
echo memory_get_peak_usage(); //147072

以上执行unset($a, $b)后再申请$c变量,内存峰值从147048增长到147072,共增长24B,但我可以保证$c变量里面的$_SERVER变量值内容不止24B

接下来尝试将unset语句注释掉,我看到的是从147000增长到147408,共增长408B

这里意味着什么?就是说当销毁a b两个变量后,腾出的空间用在c身上了,所以新增c时所扩展的内存空间仅只有24B

如果不执行unset的话,那就没私房钱可以花了


要点速读

  • 手写unset语句把变量一个个销毁真心累,一般人都不会做,除非必须unset

  • 函数/方法运行结束后,这个作用域里申请的变量都会被自动销毁,等于自动帮你unset了,所以在作用域运行到结尾时加unset语句绝对是多余的操作!

  • 适当封装函数,这些函数调用完后变量自动销毁,就可以腾出内存给下一个函数利用这些内存,内存峰值增长就可以放缓

  • 多封装代码,上百行处理的代码必然导致内存峰值持续增长,这种代码必须封装优化才能在大并发情况下有更好的性能表现


正文

明白了内存峰值后,我想很多程序员会想着:哎呀原来unset后,新变量会利用旧变量的内存空间呀,这样就可以缓解内存峰值上升的速度了,那我得积极地去unset呀!

然而你看过有哪个开源程序真的去积极地unset变量的呢?————答案是因为根本没必要这样做


手动写销毁的变量既繁杂又让人感到心累

随便打开几份你手上现有的代码,观察下里面的变量,在这个变量已经不用的位置开始,你为它添加unset处理,再找找其它变量继续写unset语句……

估计你写到第三个变量就会开始心累了,这有完没完啊,是不是每个变量用完都要你去unset呢?

但至少这个内心感受已经证明了手动写unset代码去销毁每一个变量是不靠谱的!————PHP可以为你自动unset


函数结束后会自动销毁变量

function test(){
	$a = array_fill(0, 1000, time());
}

echo memory_get_peak_usage() . PHP_EOL; //123440 注意下一次的峰值

test();
echo memory_get_peak_usage() . PHP_EOL; //172056 test函数增加了大数组,增值较上次激增

test();
echo memory_get_peak_usage() . PHP_EOL; //172152 峰值增长变得很微小了

第二次增长微秒,可以判断这些增长多数是由于函数调用的运算成本用的,然而再次进入test函数里再申请变量时没有发生二次激增

原因就是第一次test函数调用结束后,test里面的变量a已经被自动销毁了,PHP把这些内存暂时保管起来,下次再有需要新增变量,就优先从使用这些腾出来的变量空间

所以上面用test函数的代码将比以下这些不用test函数的代码内存峰值更低:

echo memory_get_peak_usage() . PHP_EOL; //120272

$a = array_fill(0, 1000, time());
echo memory_get_peak_usage() . PHP_EOL; //171152 突然产生个大数组,峰值必然激增

$b = array_fill(0, 1000, time());
echo memory_get_peak_usage() . PHP_EOL; //223416 由于没有删除变量腾出空间,二次产生大数组,于是又一次峰值激增

小结

通常情况下适当封装函数调用,反而能节省内存,因为函数调用完就会释放变量空间留给其他新增变量使用

这一点我认为非常重要,通常很多才做两三年的普通程序员都觉得老是封装,调来调去好麻烦

其实纵观各大开源框架的代码,都是一层层互相调用,一层接一层,每层只做自己的事,做完就返回,释放空间,把这些空间留给其他环节使用,这样既令程序结构清晰而又节省了内存

但是其实并不是所有封装都必然能节省内存的,要看具体情况,你看看代码的主要逻辑,特大数组/对象的处理加以思考就可以判断出你目前的情况使用封装会不会有利于内存使用

然而很经常见到一些代码是在一个控制器的方法里写几百行代码肯定是浪费内存的,不如将主要的处理环节封装成各个函数,让一个环节工作完就能空出一些内存不更好?否则这必然会导致内存峰值持续增长,这种代码必须优化才能在大并发情况下有更好的性能表现

具体哪些代码要封装哪些不要封装难以一概而论,需要看你的情况,一般情况下都是按照代码责任范围来划分一个个类和方法


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值