首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHPExcel 記憶體洩漏根因與解決方案:手動斷開循環引用

     編輯:PHP 綜合知識

分析 PHPExcel 在單一 HTTP 請求中重複建立多個試算表物件導致記憶體洩漏的根本原因:工作表與儲存格雙向參考形成循環引用,導致 GC 無法回收。提供在 PHPExcel_Worksheet 與 PHPExcel 類別中新增 Destroy() 方法的修正方案,並說明為何不能放在 __destruct()。

問題現象

  • 循環建立 PHPExcel + PHPExcel_Writer_Excel5 產生多份報表。
  • 記憶體隨迭代遞增,請求結束前不釋放,甚至觸發 memory_limit 錯誤。
  • 版本 1.6.5 仍存在(截至文件撰寫時)。

根因分析

// 簡化結構示意
PHPExcel
  └─ _workSheetCollection[] → PHPExcel_Worksheet
         └─ _cellCollection[] → PHPExcel_Cell
                └─ _parent → 回指 PHPExcel_Worksheet  (循環引用)
                └─ _worksheet → 回指 PHPExcel_Worksheet (循環引用)
PHP 的參考計數 GC 無法處理跨物件循環引用(需 gc_collect_cycles()),
但在請求結束前物件仍被變數參考,不會觸發循環 GC。

修正方案:新增 Destroy() 方法

PHPExcel_Worksheet 類別

public function Destroy() {
    foreach ($this->_cellCollection as $index => $dummy) {
        $this->_cellCollection[$index] = null; // 斷開儲存格參考
    }
}

PHPExcel 類別

public function Destroy() {
    foreach ($this->_workSheetCollection as $index => $dummy) {
        $this->_workSheetCollection[$index]->Destroy(); // 遞迴清理工作表
        $this->_workSheetCollection[$index] = null;     // 斷開工作表參考
    }
}

使用方式

<?php
// 在迴圈中產生多份報表
foreach ($reports as $report) {
    $objPHPExcel = new PHPExcel();
    // ... 填充資料、設定樣式 ...
    $writer = new PHPExcel_Writer_Excel5($objPHPExcel);
    $writer->save($filename);

    // 關鍵:手動觸發清理
    $objPHPExcel->Destroy();
    unset($objPHPExcel, $writer);
    
    // 可選:強制觸發循環 GC(PHP 5.3+)
    gc_collect_cycles();
}
?>

為什麼不用 __destruct()?

  • __destruct() 在物件參考計數歸零時才會被呼叫。
  • 循環引用導致參考計數永遠 > 0,__destruct() 不會被觸發。
  • 必須在邏輯上明確知道「此物件不再使用」的時間點,主動呼叫 Destroy()。

替代方案比較

方案優點缺點
手動 Destroy()即時釋放、可控需修改源碼/繼承覆寫
gc_collect_cycles()無需修改庫效能影響大、時機不可控
分批請求/CLI 腳本進程隔離、自動回收架構調整成本高
改用 PhpSpreadsheet官方繼任者、修復此問題需遷移代碼

建議

新專案請直接使用 PhpSpreadsheet(PHPExcel 官方繼承者),已內建解決循環引用問題。維護舊專案時,可套用上述 Destroy() 修正或定期呼叫 gc_collect_cycles() 緩解。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved