巨大JSONファイルが開けないとき:サイズ別ツールの選び方
要点:数十MBのJSONエクスポートはテキストエディタやオンラインツールで頻繁に固まりますが、解析自体は数百ミリ秒で終わります。45MB・50万件のサンプルで各段階の処理時間とメモリを実測し、本当のコストは行数とノード数であることを示した上で、ファイルサイズ別のツール選択表を提示します。オンライン表示、コマンドライン抽出、ストリーミング処理、デスクトップアプリの役割分担がわかります。

45MBのJSONファイルの解析は249ミリ秒で終わります。30秒待たされる原因は、500万行のテキストと350万個のツリーノードの描画です。この二つを混同すると、いつも間違った犯人を探すことになります。
一、まず実測:解析はボトルネックではない
数十MBの.jsonエクスポートをダブルクリックし、エディタが数十秒回り続けると、「JSONの解析が遅い」と思いたくなります。実測結果は逆です。本機(Node 22)での計測結果を示します。サンプルは50万件・各5フィールド(id、email、active、score、tags)のレコードで、圧縮状態で45MBです。
| 操作 | 実測結果 |
|---|---|
JSON.parse による全量解析 |
249 ms |
JSON.stringify(data, null, 2) の整形出力 |
215 ms |
| 整形後のテキスト | 71 MB、5,000,001 行 |
| ツリーノード総数(レコード+フィールド) | 約 3,500,001 個 |
| 解析後のNodeプロセスRSS | 313 MB(ファイルの約7倍) |
解析は200ミリ秒強で完了しています。つまり、マウスを放す前に解析器の仕事は終わっているのです。その後に固まる十数秒から数分間は、すべて「結果を人間に見せる」層で発生します。テキストエディタは500万行をレイアウトし、一行ずつシンタックスハイライトを適用し、undoバッファを維持しなければなりません。ツリービューアは350万個のノードそれぞれにDOM要素を作ります。ブラウザは10万ノード規模で明らかに苦しくなり、数十万で応答を失います。
この結論が指す修復の方向はまったく別のものです。描画と戦うのではなく、全量描画をしないツールに乗り換えることです。
二、なぜ固まるのか:行数とノード数が本当のコスト
ファイルサイズは表象にすぎず、ツールを壊すのは二つの派生数値です。
- 行数。45MBのコンパクトなJSONは整形すると500万行になります。エディタはファイルを開いた瞬間、どの画面を見ていても全文の行構造を作り、ハイライト状態を決め、バッファを割り当てます。コストは総行数に比例します。
- ノード数。50万レコード ×(1オブジェクト+5フィールド)で350万ノードです。全ツリーを構築してから表示するビューアは、展開が1階層でも全部でも同じメモリと時間を支払います。
ここから直感に反する結論が二つ導けます。先に整形してから見るのは足を引っ張る行為です。整形で45MBは71MBに増え、行数は40倍になります。ファイルをcatでターミナルに流すのも同様です。スクロールバックバッファが新たなボトルネックになるので、先頭の数百バイトだけ見たいならhead -c 2000で十分です。
三、サイズ別ツール選択:意思決定表
体量によって適切なツールはまったく変わります。上記の実測と各ツールサイトの公開ガイド(OpenFormatterはオンラインツールの快適域を2-5MB、PureJSONは500MB解析後に2-4GBのメモリと推定)を合わせると、次のような決定表になります。
| ファイルサイズ | 第一選択 | 理由 |
|---|---|---|
| < 2 MB | どんなオンラインツールでも | 即座に表示。/parse型のツリー表示+検証が一歩で済む |
| 2 – 20 MB | ローカルエディタ(VS Code等)またはブラウザローカルツール | オンラインツールが劣化し始める。ローカル処理ならアップロード不要 |
| 20 – 100 MB | CLIのjq調査+仮想化ツリービューア | エディタが重くなる。jqなら数秒で任意部分を抽出 |
| 100 MB – 1 GB | jq --stream / ijson のストリーミング処理 |
通常解析のメモリはファイルの約7倍に増える。収まらなければストリーミング |
| > 1 GB または追記が続く | フォーマット変更:NDJSON / CSV / Parquet | 巨大な単一配列は完全解析まで最初のレコードが使えない。フォーマット自体が不適 |
当サイトの[/parse](/parse)は第一層のツリー表示と検証を担います。より大きなファイルはデスクトップアプリでローカルに開き、数十MBのデータをWebページに貼るのを避けましょう。
四、ファイルを開かず、まず構造を尋ねる
見知らぬ巨大JSONをもらったら、最初にやるべきは開くことではなく、数秒で構造を掴むことです。jqなら200msで三つの質問に答えられます。
# トップレベルは配列かオブジェクトか? レコード数は?
jq 'if type == "array" then {type: "array", count: length} else {type: "object", keys: (keys|length)} end' huge.json
# 最初のレコードの中身(1件だけ、流れない)
jq '.[0]' huge.json | head -40
# 全フィールド名(最初のレコードから取得)
jq '.[0] | keys' huge.json
構造がわかれば、大半の要求は「あるフィールドの抽出」に変わります。ファイルを開く必要すらありません。たとえば全ユーザーのメールアドレスだけ欲しい場合:
jq -r '.[].email' huge.json | less
# フィルタを重ねる
jq -r '.[] | select(.active == true) | .email' huge.json
ルールは単純です。「見る」なら仮想化描画のビューア、「探す・取る」ならjq。二つの役割を一つのツールで混ぜないことです。
五、メモリの急坂:通常モードとストリーミングで150倍差
同じ45MBサンプルで、/usr/bin/time -vによりjqの二モードのメモリピークを計測しました。
| モード | コマンド | メモリピーク(RSS) | 時間 |
|---|---|---|---|
| 通常モード(全量ロード) | jq '.[0].email' sample.json |
542 MB | 0.7 s |
| ストリーミング | jq --stream 'select(.[0][1]=="email") | .[1]' sample.json |
3.6 MB | 2.5 s |
通常モードはファイル全体を読み込みオブジェクトグラフを構築するため、542MBのピークは「オブジェクトグラフ ≈ テキストの7-12倍」という経験則を裏付けます。--streamは入力をトークン単位で消費し、メモリは一桁MBに一定です。代償は総時間がやや長いこと。GB級になると、この差が「動く」と「OOMキラーに殺される」の分かれ目になります。
# メモリを超えるファイル:全量をロードしない唯一の方法が --stream
jq --stream -n 'fromstream(1|truncate_stream(inputs))' huge.json
Python側の同等物はijsonです。ijson.items(f, 'item')が配列要素を一件ずつ生成し、同じく定数メモリで動きます。これらのツールの共通点はランダムアクセスの放棄です。ストリームは前方向きの一回きりで、二回目はもう一度流すしかありません。
六、ブラウザでどこまで見られるか:ローカル処理の限界
オンラインツールが使えないわけではありません。限界があるだけです。現代のブラウザはタブあたりおよそ1-4GBのメモリを使え、45MBファイルの解析+ツリー描画は通常持ちこたえます。数百MBになると崖の縁です。オンラインツールを選ぶなら三点を確認してください。
- 処理がローカルで完結する(貼り付けた内容がサーバーに送られない)。データを守り、アップロード時間も節約します。当サイトの全ツールは純フロントエンド実装で、オフラインでも動きます。
- 解析がWeb Workerで動く。メインスレッドが固まらず、止まってもタブを閉じるだけで済みます。
- ツリーが仮想化または遅延展開。折りたたまれた分支のコストはほぼゼロで、展開した分だけ描画されます。
当サイトでは、[/parse](/parse)のツリー表示が数十MBまでのファイルの構造確認やエラーの行列特定に向きます。それ以上はjqかデスクトップアプリに任せ、ブラウザは小さな断片の確認に使います。
七、フォーマットを変えれば問題は半分消える
ファイルの生成側に手を入れられるなら、より強力なビューアに乗り換えるより、「一つの巨大配列」に育たないようにする方が正解です。
- NDJSON/JSONL:1行1レコード。追記しても全ファイルを書き直さず、
head、grep、tail、splitなどテキストツール一式がそのまま使え、ファイルが途中で切れても最終行しか失いません。当サイトのJSONLハンドブックに壊れた行の調査手順があります。 - ページング出力:10万件を返すAPIはページング引数を受け、一度に100件ずつ返すべきです。双方に優しい設計です。
- 表型データにJSONを使わない:1行1レコードでフィールドが固定・ネストなしなら本質は表です。CSVやParquetの方が小さく、生態系も豊かです。
覚えておきたい実測値があります。50万件の配列を途中で切ると、前半はまだ有効なNDJSONファイルです(半分のレコードが生き残る)。同じ内容の単一巨大配列は全滅です。501件中500件の復旧は、0件よりはるかにましです。
八、チェックリスト
- まず
jq '.[0] | keys'や500件のサンプリングで構造を掴んでから処理方法を決める; - 見るためだけに先に整形しない——行数が倍増するだけで遅くなる;
- 2MB未満はオンラインツリー表示、20MB超はコマンドライン、1GB級は常に
--streamかijson; - オンラインツールがローカル処理か(貼り付け内容がブラウザ外に出ないか)確認する;
- 生成側を変えられるならNDJSONかページングに切り替え、巨大な単一配列を源でなくす。
よくある質問
なぜエディタで開くと数十秒かかるのに、プログラム内の解析は200ミリ秒程度なのですか?
解析はネイティブコードがテキストをデータ構造に変える処理で、45MBサンプルで実測249msでした。エディタが固まるのは表示層です。500万行のレイアウト・ハイライト・undoバッファ、そして350万ノードのDOM構築。仮想化ビューアに乗り換えるかjqで直接抽出すれば回避できます。
jqは1GB超のJSONでメモリオーバーしますか?
通常モードではします。ドキュメント全体をロードし、オブジェクトグラフのピークはファイルの約7-12倍です。--streamを付けると入力をトークン単位で消費し、45MBの実測でピーク3.6MB。ファイルがどれだけ大きくてもメモリは増えません。代償は順方向の一回走査だけということです。
オンラインJSONツールで見られるファイルの上限は?
多くのオンラインツールの快適域は2-5MBで、10MBを超えると明確に劣化します。ハード上限はタブのメモリ(約1-4GB)ですが、数十MBの時点で解析+ツリー構築の待ち時間が苦痛になります。20MB超はローカルツールかCLIを推奨します。
巨大JSONから特定フィールドの値だけ見るには?
ファイルを開かずjqで抽出します。jq -r '.[].email' huge.jsonですべてのemailが出力され、jq -r '.[] | select(.active == true) | .email'で絞り込めます。先頭数件ならjq '.[0:5]' huge.json。ファイルを開くより桁違いに速いです。
先に整形してから開くのは効果がありますか?
ありません。コンパクトな45MBが71MB・500万行になり、エディタとビューアに純粋な負担を足すだけです。整形が価値を持つのは、人が読むためで、かつファイルが十分小さい場合だけです。