IT

TCPDFで大量PDFを一括生成するとメモリ不足になる問題と解決策

背景

PHP+CodeIgniter から TCPDF を利用して PDF 出力する処理を実装した。

少量のデータなら問題なく動作してたけど、大量の請求先を一括で生成しようとすると Allowed memory size exhausted エラーが発生し、メモリ不足に陥るケースがあった。

以下に、発生した問題の原因と、実際に採用した解決策についてまとめる。

症状

数百の PDF を一括で出力する処理では、次のようにループのたびに new Pdf() で TCPDF インスタンスを生成し、$pdf->Close()unset() を呼び出していた。

foreach ($data as $billing_cd => $summary) {
    // … 請求書ごとのデータ設定 …
    $pdf = new Pdf(array('P','mm','A4',true,'UTF-8'));
    // PDFに内容を書き込む
    // …
    $pdf->Output($filename,'F');
    $pdf->Close();
    unset($pdf);
    gc_collect_cycles();
}

しかし、生成件数が多くなると PHP のプロセスが大量のメモリを消費し、メモリ上限に到達して処理が中断した。

原因

TCPDF はフォントや画像などをクラスの内部にキャッシュしていて、デフォルトではコンストラクタで register_shutdown_function() を登録していて、各インスタンスの デストラクタはスクリプト終了時に一度だけ実行される 仕様らしい。

ループの中で Close() を呼んでも PDF 出力を終了するだけで、内部のキャッシュやフォント情報が解放されない。

その結果、インスタンスを生成するたびにメモリが増え続ける ことになる。

FVue の記事ではこの挙動を検証しており、1,000 件の TCPDF オブジェクトを生成するだけでメモリが 120MB 以上に達することが報告されている。

また、SourceForge のフォーラムでも同様の相談があり、開発者から「同じインスタンスを使い回すか _destroy() を明示的に呼び出すように」というアドバイスが出ている。

解決策

1. デストラクタを明示的に呼び出す

TCPDF では __destruct()(内部では _destroy() を呼び出す)を手動で実行することでキャッシュを解放できる。

PDF を出力した後に以下のように記述する。

$pdf->Output($filename, 'F');
// キャッシュを解放
$pdf->__destruct();
unset($pdf);
gc_collect_cycles();

_destroy(true) を直接呼び出しても同様の効果がある。

これにより各ループ後にメモリ使用量がほぼ元に戻り、大量出力でもエラーが出なくなった。

2. インスタンスの再利用

出力ごとに新しい TCPDF オブジェクトを生成するのではなく、単一のインスタンスを使い回す 方法もある。

ページを追加するだけであれば AddPage() を呼び出せば済むので、フォントの読み込みなどの初期化処理が一度で済み、メモリ使用量も抑えられる。

3. その他の工夫

  • ディスクキャッシュを利用する: tcpdf_config.phpK_PATH_CACHE を適切なディレクトリに設定し、コンストラクタの $diskcache 引数を true にすると、フォントや画像をディスクにキャッシュできる(古いバージョン向けの設定だが一括出力時のメモリ対策になる)。
  • フォント設定の最適化: 日本語を含まない帳票では setFontSubsetting(false) を使用し、Unicode ではなくコアフォントや ISO‑8859‑1 等に切り替えるとメモリ使用量を減らせる。
  • 処理の分割: 一度にすべてを生成せず、一定件数ごとに別プロセスとして呼び出す(CLI やバッチ処理)とプロセス終了時に完全にメモリが解放される。

まとめ

TCPDF で大量の PDF を連続生成する際に Close()unset() だけではメモリが解放されず、プロセスがメモリ不足に陥ることがある。

原因はデストラクタが shutdown 時まで遅延実行されることにあり、手動で __destruct()(または _destroy())を呼び出すことでキャッシュを解放できる ことが分かった。

インスタンスを使い回す、ディスクキャッシュを有効にする、フォント設定を見直すといった工夫も合わせることで、大量出力処理が安定して動作するようになった。

今回の経験が、同じように TCPDF のメモリ消費に悩む人の参考になればうれしいです。

管理者

This website uses cookies.