「メインスレッド処理の最小化」原因特定と改善方法を解説

「メインスレッド処理の最小化」原因特定と改善方法を解説

アプリの体感品質を左右する最大の要因の一つが「メインスレッドの詰まり」です。

本記事では、“メインスレッド処理の最小化”を軸に、なぜUIがカクつくのか・どこで時間を消費しているのかを原因から特定し、計測→切り分け→改善までの現実的な手順を整理します。

今日から使える指標と実装上の勘所も解説します。

「メインスレッド処理の最小化」とは?

「メインスレッド処理の最小化」とは、UI描画やユーザー入力を担当するメインスレッドから、重い処理(ネットワーク待ち、DBアクセス、画像デコード、JSONパース、大量のレイアウト計算、同期I/O、複雑な計算など)をできるだけ排除し、“メインスレッドは短く・軽く・すぐ戻る”状態を徹底する考え方です。

目的は、フリーズやカクつき(ドロップフレーム)、タップ遅延、スクロールの引っ掛かりを減らし、体感速度と安定性を上げること。

具体的には、処理の非同期化・バックグラウンド移譲、必要データの事前計算/キャッシュ、UI更新の間引き、同期APIの置換、描画コストの削減で実現します。

メインスレッド処理の最小化のカテゴリ項目

PageSpeed InsightsでURLを分析すると「パフォーマンスの問題を診断する>インサイト」の箇所に表示されます。

ここの赤枠に書いてある部分をまずは解説していきます。

Script Evaluation(スクリプト評価)

Script Evaluationは、読み込んだJavaScriptを実際に動かしている時間です。

Dev toolではパフォーマンスのボトムアップにある「スクリプト評価」の数値が評価に値します。

ここが長いとメインスレッドが塞がれ、表示や操作が重くなります。

主に下記のような要素に時間がかかります。

  • 関数の実行、初期化処理、イベント処理
  • React/Vue等の初期レンダリング・差分計算
  • JSONのパース、データ整形、ループ、重い計算
  • 依存ライブラリの初期化(analytics、広告、A/B、UIライブラリ等)

Script Parsing & Compilation(解析&コンパイル)

JavaScriptを読む(parse)→実行できる形に変換(compile)する時間です。

Dev toolではパフォーマンスのボトムアップにある「スクリプトコンパイル」の数値が評価に値します。

下記のようなケースが該当します。

  • JSファイルが大きい、数が多い
  • minifyされていても“量”が多いと増える
  • 古いブラウザ向けの重いトランスパイル(polyfill盛り)でも増えがち

ここが大きい=まず読み込んで実行準備するだけで詰まっている状態です。

Garbage Collection(ガベージコレクション)

不要になったメモリを回収する時間(GC)です。

Dev toolではパフォーマンスのボトムアップにあるGC系の数値が評価に値します。

例えば「メジャーGC」や「マイナーGC」といったものがあります。

下記のようなケースが該当します。

  • 大量のオブジェクト生成(配列・オブジェクトを作りまくる)
  • 大きいデータを何度もコピーする
  • DOMノードを作って捨ててを繰り返す

ここが大きい=メモリの使い方が荒い可能性があります。

Parse HTML & CSS(HTML/CSSの解析)

HTMLとCSSを読み取って構造(DOM)やスタイル情報を作る時間です。

Dev toolではパフォーマンスのボトムアップにある「HTMLを解析」や「スタイルシートを解析」の数値が評価に値します。

下記のようなケースが該当します。

  • HTMLが巨大(DOMが深い/ノードが多い)
  • CSSが多い、複雑、読み込みが多い

Style & Layout(スタイル計算&レイアウト)

CSSの適用計算(style)と、要素の位置・サイズの計算(layout/reflow)です。

Dev toolではパフォーマンスのボトムアップにある「スタイルの再計算」や「レイアウト」の数値が評価に値します。

下記のようなケースが該当します。

  • DOM更新が多い
  • レイアウトを頻繁に確定させる処理(例:offsetHeight や getBoundingClientRect() を更新と交互に呼ぶ)
  • 複雑なCSSセレクタ、巨大DOM

Rendering(描画)

実際に画面に塗る処理(ペイント、合成など)です。

Dev toolではパフォーマンスのボトムアップにある「ペイント」や「プリペイント」の数値を始め、「レイヤーツリーの更新」「複合レイヤー」が評価に値します。

下記のようなケースが該当します。

  • 大きい画像、重いエフェクト(blur/shadow)、巨大な固定要素、アニメーション多用などで増える

今回の数値ではかなり小さいので、描画自体は重くない寄りです。

Other(その他)

いろいろ混ざる「分類しきれない/まとめ枠」です。

代表例

  • ブラウザ内部の処理や計測のオーバーヘッド
  • タスクの待ち・スケジューリング関連
  • いくつかの小さい処理が合算されて “Other” に入るケース

Otherが大きいときは、Performanceタブで“Main”の長いタスクを見て、何が入っているかを特定するのが定石です(PSIだけだと正体が見えにくい)。

メインスレッドが詰まると起きること

メインスレッドが詰まる(一定時間処理を占有する)と、UI描画と入力処理が次のフレームに間に合わず、体感上の不具合が連鎖します。

具体的には、タップしても反応が遅れる・スクロールが引っ掛かる・アニメーションがカクつく(フレーム落ち)といった“jank”が発生し、最悪は画面が固まったように見えます。

さらに詰まりが長いと、OS側で「応答なし(ANR相当)」判定やハング解析対象になり、クラッシュや離脱率増加にもつながります。

Webでも同様に、メインスレッドを50ms以上占有する“Long Task”が増えると操作への応答が遅れやすく、ユーザー体験を大きく損ねます。

メインスレッドの処理が重くなる原因のパターン

メインスレッドの処理が重くなるのはパターン化されています。

主に下記の原因で重くなることがほとんどです。

まずは原因のパターンを把握して、改善に臨みましょう。

CPU計算が重い(パース、暗号化、画像処理、Diffなど)

CPU計算が重いケースは、メインスレッド上で「純粋な計算時間」が積み上がり、入力処理・レイアウト・描画の割り込みができなくなるのが本質です。

典型例は、巨大JSONのパース、暗号化/署名、画像のデコード/リサイズ/フィルタ、差分計算(Diff)、ソートや正規表現の多用など。

これらがフレーム境界(例:60fpsなら約16.6ms)をまたぐとスクロールがカクつき、50msを超えると“長いタスク”として操作レスポンスの遅延が目立ちます。

同期I/Oが重い(DB、ファイル、ネットワーク待ち)

同期I/Oが重いケースは、処理時間そのものより「待ち」がメインスレッドを占有してしまうのが致命的です。

DBクエリ、ファイル読み書き、設定読み込み、ネットワークの同期呼び出しなどをメインスレッドで行うと、その間UIスレッドはイベントループを回せず、タップ反応の遅延・スクロール停止・画面フリーズとして現れます。

Webでも同様に、メインスレッドを50ms以上塞ぐと“Long Task”となり応答性が悪化します。

UI構築/レイアウト/再描画が重い(不要な更新、過剰なView/Widget)

UI構築/レイアウト/再描画が重いパターンは、CPU計算やI/Oと違って「UIを更新しすぎる/作りすぎる」ことで、メインスレッドのmeasure/layout/draw(あるいはWidget build)が膨らみ、フレーム予算(60fpsなら約16.6ms)を超えてカクつくのが本質です。

典型例は、状態変更のたびに画面全体を再計算する、ネストが深いView/Widget、巨大リストの非効率な更新、不要なアニメーション・影・透過、そして同じピクセルを何度も描くOverdrawです。

Androidでは「Profile GPU Rendering(HWUI)」でフレーム時間と過負荷を可視化でき、Overdrawも診断できます。

GC/メモリ圧迫が誘発する停止(間接的に“メインが止まる”)

GC/メモリ圧迫が原因の停止は、処理を直接メインスレッドに書いていなくても、大量の確保・解放(アロケーション)やメモリ逼迫が引き金になって、結果として“メインが止まったように見える”タイプのjankです。

代表例がGCの停止時間(Stop-the-world的な一時停止)で、短くてもフレーム予算(16.6ms)を跨ぐとスクロールやアニメが途切れます。

WebでもGCはパフォーマンストレース上で要因として観測でき、メインスレッドが詰まる一因になります(GC改善が「jank削減」の文脈で語られる)。

メインスレッドの処理が重くなる原因を特定する方法

まずはDev toolで、パフォーマンスを開き、読み込みのボタンを押しましょう。

サイトによって読み込み時間はまちまちですが、今回は20秒ほど読み込みし、停止ボタンを押しました。

読み込み終わると下記のような画面が出てくるので、メインのタブと下部にあるボトムアップを開いておきましょう。

ここまで準備できたら「メインスレッド処理の最小化のカテゴリ項目」で解説した該当項目をボトムアップの中から探してみましょう。

下記の画像例で解説していくと、合計時間で処理時間のかかっているものに並び替えて探してみました。

一番上に「スクリプトの評価」という該当項目が出てきているのでここをクリックします。

すると「メイン」の箇所に該当する部分に色がつきます。(小さくて見にくいですが矢印の部分です)

検索のフィルタなどで検索すると簡単に該当項目が探せます。

さらに下記の画像のように詳しく見たい箇所に絞り込むと該当の箇所がわかりやすくなります。

該当の箇所をクリックしてあげると、下記のようにどの項目に何秒かかっているかと、対象のURL(ファイル)が特定できます。

特に合計時間が大きいものから確認し対処していってあげると、表示速度に対するインパクトが大きく感じると思います。

「メインスレッド処理の最小化」の改善方法

不要なコードを削除する

不要なコード削除は、メインスレッドで走る処理量を直接減らす最短ルート。

まず計測で実行頻度が高い関数・重い初期化・未使用モジュールを特定し、使われない機能フラグ、デバッグ処理、重い依存(日時・グラフ等)を削る。

次に、読み込むだけで副作用が起きるコード(グローバル初期化、イベント登録、ポリフィル全盛り)を見直し、必要時にだけロードする。

Tree Shakingが効く形(ESM、sideEffects設定)に整え、共通バンドルに不要機能を混ぜない。最後にCoverageで未使用率が下がったか確認する。

ロングタスクを分割する

ロングタスク分割は、1回で完了させようとせず処理を「小さな塊」に切ってフレーム間へ散らす改善。

まず計測で50ms超の区間を特定し、ループ・大量DOM生成・JSON整形・画像デコード等を10〜20ms目安でチャンク化する。

各チャンク後はrequestAnimationFrame/setTimeout(0)/idle時間などで次回に回し、スクロールや入力を最優先にする。

UI更新も一括反映(バッチ)にしてレイアウト計算を減らす。

進捗はインデックスで保持し、途中で中断・再開・キャンセルできるようにすると体感が安定し、さらに有効となります。

実行タイミングをずらす

実行タイミングをずらすとは、初期表示や操作中に走る不要処理を「後回し」にしてメインスレッドの混雑を避けること。

起動直後は描画に必要な最小処理だけに絞り、ログ送信・プリフェッチ・解析・キャッシュ整理などは画面表示後や初回操作後へ移す。

入力・スクロール中は重い処理を止め、デバウンス/スロットルで発火回数も削減。優先度の低い処理はidle時間に実行し、必要になった瞬間にだけロード/初期化する(遅延ロード)。

これで体感の引っかかりを減らせる。

重い計算はメインスレッドから追い出す

重い計算をメインスレッドから追い出すには、まず計測で50ms超の処理を特定する。

次にその処理をUI依存(描画・DOM操作・View更新)と計算部分に分離し、計算は別スレッド/ワーカー(Web Worker、AndroidのCoroutine/Executor、iOSのGCD)で非同期実行する。

メインは入力データ送信と結果受信、差分だけのUI反映に限定。

さらにキャンセル・タイムアウト、例外処理、開始/終了/所要msのログを入れて安定運用する。

無駄な再計算・再描画を削る

無駄な再計算・再描画削減は、同じ結果を何度も作らないことが要点。

まず計測で再レンダー/再レイアウトが多い箇所を特定し、入力が変わらない計算はキャッシュ(メモ化)する。UI更新は一括(バッチ)で行い、状態更新の連発を避ける。

リストは差分更新と仮想化、画像は縮小済みを使いデコードを抑える。

DOMはまとめて操作し、読み取りと書き込みを混ぜない(レイアウトスラッシング回避)。不要なイベント発火はデバウンス/スロットルで回数を減らす。

I/O をメインで待たない

I/Oをメインで待たないとは、ネットワーク・DB・ファイル読み書きなどの待ち時間でUIを止めないこと。

まず同期APIやブロッキング処理を洗い出し、非同期APIへ置換してバックグラウンドで実行する。

メインはローディング表示とキャンセル受付、完了時に結果だけ反映する。

連続リクエストはまとめる/キャッシュし、再試行やタイムアウトも設定。

必要直前に取りに行かず、予測できるものは事前取得しつつ、初期表示に不要な通信は後回しにする。

ログに開始/終了/失敗理由/所要msを残し、遅いI/Oを継続監視する。

「メインスレッド処理の最小化」まとめ

メインスレッド処理の最小化は、一度行えば終わりではなく、継続的な改善が必要な取り組みです。

Webサイトの表示速度改善は、ユーザーエクスペリエンスの向上とSEO効果の両方を得られる重要な施策です。

特に、メインスレッド処理の最小化は、技術的な知識があれば大きな効果を期待できる改善項目の一つです。

今回ご紹介した方法を参考に、まずはできることから始めてみてください。小さな改善でも積み重ねることで、大きな効果を得ることができます。

表示速度の改善は、単なる技術的な問題ではなく、ビジネス成功のための重要な要素です。

ユーザーのためにも、検索エンジンのためにも、そして自社のビジネスのためにも、継続的な改善に取り組んでいきましょう。

記事を書いた人

井上寛生

井上寛生

LandingHub 執行役員 / 事業責任者 / 技術責任者

大学院では情報工学を専攻し、修了後に株式会社TeNへ新卒入社。当時は社内唯一のエンジニアながら、開発部門をゼロから立ち上げ、採用・育成を一手に担い、全員が未経験からスタートした精鋭エンジニアチームを組成。2021 年にはWEBサイト高速化プラットフォーム「LandingHub」を立ち上げ、プロダクトオーナー兼事業責任者として企画・開発・グロースを牽引。現在は執行役員として、会社の技術戦略と事業成長の双方をリードしている。
コラム一覧に戻る