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 相关文章推荐
网友原创的PHP模板类代码
Sep 07 PHP
php读取xml实例代码
Jan 28 PHP
理解php Hash函数,增强密码安全
Feb 25 PHP
解析smarty模板中类似for的功能实现
Jun 18 PHP
php创建sprite
Feb 11 PHP
PHP函数http_build_query使用详解
Aug 20 PHP
php实现求相对时间函数
Jun 15 PHP
php判断访问IP的方法
Jun 19 PHP
php+ajax实现无刷新的新闻留言系统
Dec 21 PHP
php接口实现拖拽排序功能
Apr 23 PHP
PHP+redis实现的悲观锁机制示例
Jun 12 PHP
laravel中数据显示方法(默认值和下拉option默认选中)
Oct 11 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 addslashes和mysql_real_escape_string
2010/01/24 PHP
php上传文件,创建递归目录的实例代码
2013/10/18 PHP
ThinkPHP3.1查询语言详解
2014/06/19 PHP
PHP使用Mysqli类库实现完美分页效果的方法
2016/04/07 PHP
PHP两种实现无级递归分类的方法
2017/03/02 PHP
PHP ob缓存以及ob函数原理实例解析
2020/11/13 PHP
Js动态创建div
2008/09/25 Javascript
在JavaScript中,为什么要尽可能使用局部变量?
2009/04/06 Javascript
jQuery对表单元素的取值和赋值操作代码
2011/05/19 Javascript
jquery实现类似淘宝星星评分功能实例
2014/09/12 Javascript
AngularJS入门教程之Scope(作用域)
2016/07/27 Javascript
如何解决IONIC页面底部被遮住无法向上滚动问题
2016/09/06 Javascript
详解Vuex中mapState的具体用法
2017/09/28 Javascript
使用DataTable插件实现异步加载数据
2017/11/19 Javascript
js实现多个标题吸顶效果
2020/01/08 Javascript
详解Vue串联过滤器的使用场景
2020/04/30 Javascript
vue 插槽简介及使用示例
2020/11/19 Vue.js
[01:39]2014DOTA2国际邀请赛 Newbee经理CU专访队伍火力全开
2014/07/15 DOTA
Python+OpenCV实现车牌字符分割和识别
2018/03/31 Python
pycharm设置注释颜色的方法
2018/05/23 Python
Python Requests库基本用法示例
2018/08/20 Python
详解Python3注释知识点
2019/02/19 Python
基于python实现学生信息管理系统
2019/11/22 Python
利用python实现平稳时间序列的建模方式
2020/06/03 Python
葡萄牙鞋子品牌:Fair
2016/12/10 全球购物
Banana Republic英国官网:香蕉共和国,GAP集团旗下偏贵族风
2018/04/24 全球购物
澳大利亚先进的皮肤和激光诊所购物网站:Soho Skincare
2018/10/15 全球购物
Michael Kors加拿大官网:购买设计师手袋、手表、鞋子、服装等
2019/03/16 全球购物
机械电子工程毕业生自荐信
2013/11/23 职场文书
拒绝黄毒毒宣传标语
2014/06/26 职场文书
应届生求职信范文
2014/06/30 职场文书
运动员代表致辞
2015/07/29 职场文书
《神奇的鸟岛》教学反思
2016/02/22 职场文书
立秋之描写立秋的作文(五年级)
2019/08/08 职场文书
导游词之长城八达岭
2019/09/24 职场文书
教你使用一行Python代码玩遍童年的小游戏
2021/08/23 Python