ファイル処理時のブラウザのメモリ制限
ファイルはたった15 MBなのに、なぜタブのメモリが足りなくなるの?
ディスク上のファイルは圧縮された状態
誤解の正体は一文で説明できます。ファイルマネージャーに表示される数値は圧縮済みデータのサイズであり、圧縮されたままでは何も処理できません。画像をリサイズ、再圧縮、変換、解析するには、まず生のピクセルにデコードする必要があり、その生ピクセルは膨大です。
計算は単純です。メモリ上の1ピクセルは通常4バイト、赤・緑・青・アルファの4成分です。4000 × 3000の写真は1200万ピクセルなので、デコード後のビットマップは約48 MBになります。元のJPEGは4 MBだったかもしれません。作業を始める前の時点で、ファイルは12倍に膨らんでいます。
| 画像の寸法 | ピクセル数 | デコード後のビットマップ |
|---|---|---|
| 1920 × 1080 | 210万 | 約8 MB |
| 4000 × 3000 | 1200万 | 約48 MB |
| 6000 × 4000 | 2400万 | 約96 MB |
| 10000 × 10000 | 1億 | 約400 MB |
しかもコピーは1つでは済まない
現実的な処理パイプラインは、同時に複数のコピーを保持します。圧縮された入力バイト列、デコードした元のビットマップ、新しい寸法の出力ビットマップ、エンコーダーの内部バッファ、そして圧縮された結果です。プレビューを表示するツールなら、さらにもう1つ増えます。先ほどの4000 × 3000の写真なら、ピーク時の作業メモリが200〜300 MBに達するのはごく普通のことです。見た目は4 MBのファイルなのにです。
PDFのラスタライズもページ単位で同じように振る舞います。A4ページを300 DPIでレンダリングすると約2480 × 3508ピクセル、デコード後で約35 MBになり、複数ページを同時にレンダリングするツールならその分だけ倍増します。動画はさらに厳しく、デコーダーは複数の参照フレームを同時にメモリ上へ置いておく必要があります。
実際の上限がどこにあるか
- タブが使えるメモリ量を決めるのはサイトではなくブラウザで、その量はマシンの搭載メモリより小さく設定されています。他のタブが動いていれば、さらに減ります。
- 一般的な32ビットメモリモデル向けにビルドされたWebAssemblyモジュールがアドレスできるのは最大4ギビバイトまでで、ブラウザが1インスタンスあたりに許可する量はそれよりかなり少ないことも珍しくありません。
- 個々のバッファにも固有の最大長があり、それはページ全体で使えるメモリ量よりずっと小さい値です。
- モバイルOSは、大きくなりすぎたタブを警告なしに終了させます。ページ側で捕捉できるエラーも発生しません。
- グラフィックスメモリはこれとは別の、より小さなプールです。プラットフォームの最大寸法を超えるCanvasは、システムメモリがどれだけ空いていても単純に失敗します。
これらはいずれも、調べれば分かる1つの数値として公開されてはいません。ブラウザ、そのバージョン、端末、そして同時に動いているものによって変わるからです。本当の難しさはここにあります。ツールの側から、あとどれだけメモリを使ってよいかを問い合わせる手段がないのです。
スマートフォンが最も厳しい理由
スマートフォンは物理メモリが少なく、デスクトップのようなスワップもなく、OSはバックグラウンドプロセスからメモリを積極的に回収します。メッセージに返信した瞬間、ブラウザのタブはバックグラウンドプロセスになります。そのためモバイルブラウザはメモリ枠をより厳しく取り、タブを早々に破棄します。しかもその破棄は、エラーとしてではなく、ページが再読み込みされて作業内容が消えたように見えるのが普通です。
実際上の帰結として、同じファイルでもノートPCなら完了する処理がスマートフォンでは不可能ということが起こります。これはツールの欠陥ではありません。ハードウェアの境界であり、誠実な対応は途中でクラッシュすることではなく、始める前にそう伝えることです。
慎重に作られたツールの振る舞い
- メモリを確保する前に、デコード後の寸法から必要な作業メモリを見積もり、明らかに収まらない処理は断ります。
- 制約の厳しい端末では、バッチを並列に走らせるのではなく、1ファイルずつ処理します。
- ページとワーカーの間でバッファをコピーせずに転送し、大きなファイルがメモリ上に二重に存在しないようにします。
- 結果は書き出した時点で解放し、放っておくとデータを保持し続ける一時URLも破棄します。
- 形式が許す場合は、文書全体をメモリに読み込むのではなく、ページ単位・フレーム単位でストリーム処理します。
- 受け付けるピクセル数に上限を設けます。これは、小さなファイルが巨大なキャンバスに展開してタブを落とすのを防ぐ仕組みでもあります。
処理が失敗したときにできること
- 他のタブを閉じてください。同じメモリ枠を奪い合っており、ブラウザはそれを公平に分配してくれません。
- 一度に処理するファイル数を減らしてください。1件ずつのキューは時間はかかりますが、完了する見込みははるかに高くなります。
- まず寸法を小さくしてください。長辺を半分にすれば作業メモリは4分の1になりますし、そもそもそれが望んでいた結果であることも少なくありません。
- 大きなPDFは分割して、その一部ずつ処理してください。
- 最も大きな処理はデスクトップマシンで行ってください。これはローカル処理の敗北ではなく、手元のハードウェアの正しい使い分けです。
- タブを何日も開いたままなら、ブラウザを再起動してください。長時間開いたタブはメモリを溜め込みますが、読み込み直せば解放されます。
見積もりの限界
ブラウザが公開している利用可能メモリの情報は大まかなヒントだけで、まったく公開しないものもあります。したがってローカルツールが表示する数値は、OSから読み取った値ではなく、ファイル自身の寸法と処理パイプラインの保守的なモデルから組み立てた見積もりです。処理を試すかどうかを判断する材料にはなりますが、完了を保証するものではありません。決め手となる要素、つまりこの先30秒のあいだに端末の他の部分が何をするかは、ウェブページからは見えないからです。
この作業に使えるツール
出典
- W3C — WebAssembly Core Specification, memory model
- W3C — Device Memory API, and why the value is coarse
- WHATWG — HTML Standard, transferable objects