PHP内存溢出优化代码详解


Posted in PHP onFebruary 26, 2021

相信很多人做大批量数据导出和数据导入的时候,经常会遇到PHP内存溢出的问题,在解决了问题之后,总结了一些经验,整理成文章记录下。

优化点

1、优化SQL语句,避免慢查询,合理的建立索引,查询指定的字段,sql优化这块在此就不展开了。
2、查询的结果集为大对象时转数组处理,框架中一般有方法可以转,如Laravel中有toArray(),Yii2中有asArray()。
3、对于大数组进行数据切割处理,PHP函数有array_chunk()、array_slice()。
4、对于大型的字符串和对象,使用引用传递&。
5、用过的变量及时unset。
6、导出的文件格式由excel改为csv
7、ini_set(‘memory_limit',''),设置程序可以使用的内存(不建议这样做)。

内存管理

PHP的内存什么怎么管理的呢? 在学C语言时,开发者是需要手动管理内存。在PHP中,Zend引擎提供为了处理请求相关数据提供了一种特殊的内存管理器。请求相关数据是只需要服务单个请求,最迟会在请求结束时释放数据。

PHP内存溢出优化代码详解

防止内存泄漏并尽可能快地释放所有内存是内存管理的重要组成部分。因为安全原因,Zend引擎会释放所有上面提到的API锁分配的内存。

垃圾回收机制

简单说下:

PHP5.3之前,采用引用计数的方式管理。PHP中的变量存在zval的变量容器中,变量被引用的时,引用计数+1,变量引用计数为0时,PHP将在内存中销毁这个变量。但是在引用计数循环引用时,引用计数就不会消减为0,导致内存泄漏。

PHP5.3之后做了优化,并不是每次引用计数减少都进入回收周期,只有根缓冲区满额后才开始进行垃圾回收,这样可以解决循环引用的问题,也可以将总内存泄漏保持在一个阈值之下。

代码
由于使用phpexcel时经常会遇到内存溢出,下面分享一段生成csv文件的代码:

<?php

namespace api\service;

class ExportService
{

 public static $outPutFile = '';

 /**
 * 导出文件
 * @param string $fileName
 * @param $data
 * @param array $formFields
 * @return mixed
 */
 public static function exportData($fileName = '', $data, $formFields = [])
 {
 $fileArr = [];
 $tmpPath = \Yii::$app->params['excelSavePath'];

 foreach (array_chunk($data, 10000) as $key => $value) {
  self::$outPutFile = '';
  $subject  = !empty($fileName) ? $fileName : 'data_';
  $subject  .= date('YmdHis');
  if (empty($value) || empty($formFields)) {
  continue;
  }

  self::$outPutFile = $tmpPath . $subject . $key . '.csv';
  if (!file_exists(self::$outPutFile)) {
  touch(self::$outPutFile);
  }
  $index = array_keys($formFields);
  $header = array_values($formFields);
  self::outPut($header);

  foreach ($value as $k => $v) {
  $tmpData = [];
  foreach ($index as $item) {
   $tmpData[] = isset($v[$item]) ? $v[$item] : '';
  }
  self::outPut($tmpData);
  }
  $fileArr[] = self::$outPutFile;
 }
 
 $zipFile = $tmpPath . $fileName . date('YmdHi') . '.zip';
 $zipRes = self::zipFile($fileArr, $zipFile);
 return $zipRes;
 }

 /**
 * 向文件写入数据
 * @param array $data
 */
 public static function outPut($data = [])
 {
 if (is_array($data) && !empty($data)) {
  $data = implode(',', $data);
  file_put_contents(self::$outPutFile, iconv("UTF-8", "GB2312//IGNORE", $data) . PHP_EOL, FILE_APPEND);
 }
 }

 /**
 * 压缩文件
 * @param $sourceFile
 * @param $distFile
 * @return mixed
 */
 public static function zipFile($sourceFile, $distFile)
 {
 $zip = new \ZipArchive();
 if ($zip->open($distFile, \ZipArchive::CREATE) !== true) {
  return $sourceFile;
 }

 $zip->open($distFile, \ZipArchive::CREATE);
 foreach ($sourceFile as $file) {
  $fileContent = file_get_contents($file);
  $file = iconv('utf-8', 'GBK', basename($file));
  $zip->addFromString($file, $fileContent);
 }
 $zip->close();
 return $distFile;
 }
 
 /**
 * 下载文件
 * @param $filePath
 * @param $fileName
 */
 public static function download($filePath, $fileName)
 {
 if (!file_exists($filePath . $fileName)) {
  header('HTTP/1.1 404 NOT FOUND');
 } else {
  //以只读和二进制模式打开文件
  $file = fopen($filePath . $fileName, "rb");

  //告诉浏览器这是一个文件流格式的文件
  Header("Content-type: application/octet-stream");
  //请求范围的度量单位
  Header("Accept-Ranges: bytes");
  //Content-Length是指定包含于请求或响应中数据的字节长度
  Header("Accept-Length: " . filesize($filePath . $fileName));
  //用来告诉浏览器,文件是可以当做附件被下载,下载后的文件名称为$file_name该变量的值
  Header("Content-Disposition: attachment; filename=" . $fileName);

  //读取文件内容并直接输出到浏览器
  echo fread($file, filesize($filePath . $fileName));
  fclose($file);
  exit();
 }
 }
}

调用处代码

$fileName = "库存导入模板";
$stockRes = []; // 导出的数据
$formFields = [
 'store_id' => '门店ID',
 'storeName' => '门店名称',
 'sku' => 'SKU编码',
 'name' => 'SKU名称',
 'stock' => '库存',
 'reason' => '原因'
];
$fileRes = ExportService::exportData($fileName, $stockRes, $formFields);
$tmpPath = \Yii::$app->params['excelSavePath']; // 文件路径
$fileName = str_replace($tmpPath, '', $fileRes);

// 下载文件
ExportService::download($tmpPath, $fileName);

到此这篇关于PHP内存溢出优化代码详解的文章就介绍到这了,更多相关PHP内存溢出优化内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
建立动态的WML站点(一)
Oct 09 PHP
php注销代码(session注销)
May 31 PHP
php curl post 时出现的问题解决
Jan 30 PHP
php获取CSS文件中图片地址并下载到本地的方法
Dec 02 PHP
php生成随机颜色方法汇总
Dec 03 PHP
PHP之预定义接口详解
Jul 29 PHP
PHP执行linux命令常用函数汇总
Feb 02 PHP
PHP面试常用算法(推荐)
Jul 22 PHP
浅谈Laravel中的一个后期静态绑定
Aug 11 PHP
PHP实现基于状态的责任链审批模式详解
May 31 PHP
laravel接管Dingo-api和默认的错误处理方式
Oct 25 PHP
PHP7 其他修改
Mar 09 PHP
php自动加载代码实例详解
Feb 26 #PHP
PHP的重载使用魔术方法代码实例详解
Feb 26 #PHP
PHP解密支付宝小程序的加密数据、手机号的示例代码
Feb 26 #PHP
php中get_object_vars()在数组的实例用法
Feb 22 #PHP
MacOS下PHP7.1升级到PHP7.4.15的方法
Feb 22 #PHP
关于PhpStorm设置点击编辑文件自动定位源文件的实现方式
Dec 30 #PHP
Thinkphp5+Redis实现商品秒杀代码实例讲解
Dec 29 #PHP
You might like
用PHP写的MySQL数据库用户认证系统代码
2007/03/22 PHP
PHP 多维数组排序(usort,uasort)
2010/06/30 PHP
通过缓存数据库结果提高PHP性能的原理介绍
2012/09/05 PHP
php 判断过去离现在几年的函数(实例代码)
2016/11/15 PHP
PHP实现十进制、二进制、八进制和十六进制转换相关函数用法分析
2017/04/25 PHP
PHP获取远程http或ftp文件的md5值的方法
2019/04/15 PHP
简单的php购物车代码
2020/06/05 PHP
js 实现无缝滚动 兼容IE和FF
2009/07/15 Javascript
学习ExtJS TextField常用方法
2009/10/07 Javascript
jquery入门—访问DOM对象方法
2013/01/07 Javascript
原生javascript获取元素样式
2014/12/31 Javascript
JS实现点击文字对应DIV层不停闪动效果的方法
2015/03/02 Javascript
深入解析JavaScript中的数字对象与字符串对象
2015/10/21 Javascript
jQuery提示插件qTip2用法分析(支持ajax及多种样式)
2016/06/08 Javascript
JavaScript中原型链存在的问题解析
2016/09/25 Javascript
jQuery实现 上升、下降、删除、添加一行代码
2017/03/06 Javascript
JS jQuery使用正则表达式去空字符的简单实现代码
2017/05/20 jQuery
浅谈ES6 模板字符串的具体使用方法
2017/11/07 Javascript
bootstrap table支持高度百分比的实例代码
2018/02/28 Javascript
angular2路由之routerLinkActive指令【推荐】
2018/05/30 Javascript
jQuery cookie的公共方法封装和使用示例
2020/06/01 jQuery
[01:39](回顾)各路豪强针锋相对,几经鏖战四强产生
2014/07/01 DOTA
详解Python中的装饰器、闭包和functools的教程
2015/04/02 Python
Python使用Pickle库实现读写序列操作示例
2018/06/15 Python
python+flask实现API的方法
2018/11/21 Python
基于树莓派的语音对话机器人
2019/06/17 Python
python 实现方阵的对角线遍历示例
2019/11/29 Python
Python绘制二维曲线的日常应用详解
2019/12/04 Python
Pytorch 实现冻结指定卷积层的参数
2020/01/06 Python
Hotels.com中国区:好订网
2016/08/18 全球购物
英国领先的在线高尔夫商店:Gamola Golf
2019/11/16 全球购物
护理专科毕业自荐信范文
2014/04/21 职场文书
初三新学期计划书
2014/05/03 职场文书
公安机关正风肃纪剖析材料
2014/10/10 职场文书
计生个人工作总结
2015/02/28 职场文书
MySQL 字符集 character
2022/05/04 MySQL