ホームページ  >  記事  >  バックエンド開発  >  PHP のメモリ オーバーフローに関する考えについて話しましょう

PHP のメモリ オーバーフローに関する考えについて話しましょう

青灯夜游
青灯夜游転載
2020-04-04 09:21:002705ブラウズ

PHP のメモリ オーバーフローに関する考えについて話しましょう

最近大規模なデータエクスポートやデータインポートを行っていると、PHPのメモリオーバーフローに遭遇することが多いので、問題解決後に経験したことをまとめて記事にまとめて記録しておきます。 . .

最適化ポイント

  1. SQL ステートメントを最適化し、遅いクエリを回避し、合理的にインデックスを構築し、指定されたフィールドにクエリを実行します。最適化についてはここでは説明しません。

  2. クエリ結果セットが大きなオブジェクトの場合、配列に変換されます。通常、Laravel の toArray() や asArray() など、フレームワーク内に変換するメソッドがあります。 Yii2で。

  3. 大きな配列のデータをカットする場合、PHP 関数には array_chunk() と array_slice() が含まれます。

  4. 大きな文字列やオブジェクトの場合は、& を使用します。

  5. 使用された変数は時間の経過とともに設定が解除されます。

  6. エクスポートされたファイル形式が Excel から csv に変更されました。

  7. ini_set('memory_limit','')、プログラムが使用するメモリを設定します。を使用できます (推奨されません)。

#思考

メモリ管理PHP のメモリを管理するには? C 言語を学習する場合、開発者は手動でメモリを管理する必要があります。 PHP では、Zend エンジンはリクエスト関連のデータを処理するための特別なメモリ マネージャーを提供します。関連データをリクエストするには、単一のリクエストを処理するだけで済み、データは遅くともリクエストの終了時にリリースされます。

#上の画像は公式 Web サイトの説明スクリーンショットですPHP のメモリ オーバーフローに関する考えについて話しましょう

メモリ リークを防止し、すべてのメモリをできるだけ早く解放することは、メモリ管理の重要な部分です。 。セキュリティ上の理由から、Zend Engine は上記の API ロックによって割り当てられたすべてのメモリを解放します。

ガベージ コレクションのメカニズム

簡単な説明:

PHP5.3 より前は、管理に参照カウントが使用されていました。 PHP の変数は zval の変数コンテナに格納され、変数が参照されると参照カウントは 1 になります。変数の参照カウントが 0 になると、PHP はメモリ上の変数を破棄します。ただし、参照回数が循環参照されると参照回数が0にならず、メモリリークが発生します。

PHP5.3 以降、最適化が行われました。参照カウントが減少するたびにリサイクル サイクルに入るのではなく、ルート バッファーがいっぱいになってからのみガベージ コレクションが開始されます。これにより、循環参照の問題が解決され、また、メモリ リークの合計がしきい値未満のままであることも復元します。

コード

phpexcel を使用するとメモリ オーバーフローがよく発生するため、csv ファイルを生成するコードを次に示します:

<?php

namespace api\service;

class ExportService
{

    public static $outPutFile = &#39;&#39;;

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

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

            self::$outPutFile = $tmpPath . $subject . $key . &#39;.csv&#39;;
            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] : &#39;&#39;;
                }
                self::outPut($tmpData);
            }
            $fileArr[] = self::$outPutFile;
        }
        
        $zipFile = $tmpPath . $fileName . date(&#39;YmdHi&#39;) . &#39;.zip&#39;;
        $zipRes = self::zipFile($fileArr, $zipFile);
        return $zipRes;
    }

    /**
     * 向文件写入数据
     * @param array $data
     */
    public static function outPut($data = [])
    {
        if (is_array($data) && !empty($data)) {
            $data = implode(&#39;,&#39;, $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(&#39;utf-8&#39;, &#39;GBK&#39;, 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(&#39;HTTP/1.1 404 NOT FOUND&#39;);
        } 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 = [
    &#39;store_id&#39;  => &#39;门店ID&#39;,
    &#39;storeName&#39; => &#39;门店名称&#39;,
    &#39;sku&#39;       => &#39;SKU编码&#39;,
    &#39;name&#39;      => &#39;SKU名称&#39;,
    &#39;stock&#39;     => &#39;库存&#39;,
    &#39;reason&#39;    => &#39;原因&#39;
];
$fileRes    = ExportService::exportData($fileName, $stockRes, $formFields);
$tmpPath    = \Yii::$app->params[&#39;excelSavePath&#39;]; // 文件路径
$fileName   = str_replace($tmpPath, &#39;&#39;, $fileRes);

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

推奨学習:

PHP ビデオ チュートリアル

以上がPHP のメモリ オーバーフローに関する考えについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はgithub.ioで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。