谈谈关于PHP内存溢出的思考-php教程

资源魔 32 0

比来做少量量数据导出以及数据导入的时分,常常会遇到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引擎提供为了解决申请相干数据提供了一种非凡的内存治理器。申请相干数据是只要要效劳单个申请,最迟会正在申请完结时开释数据。

1.png

上图是来自于官网的形容截图

避免内存泄露并尽可能快地开释一切内存是内存治理的首要组成局部。由于平安缘由,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开发教程 php开发资料 php开发自学 内存溢出

抱歉,评论功能暂时关闭!