開発におけるモツモツ—その正体、原因、および最適化方法

著者: IT Sectr 公開日: 2026-07-28 読了時間: 8 分

モツモツ とは、モバイルアプリが遅く不定期に動作する状況をユーザーが記述するものです。つまり、通常は正常に反応しますが、突然数秒間フリーズすることがあります。技術的な文脈では、“モツモツ”は頻繁なGCポーズ、同期操作によるメインスレッドのブロック、非最適なデータ構造によって引き起こされるラグとマイクロフリーズの組み合わせを意味します。Android Performance Benchmarking Guideによると、応答時間を300msから100msに減らすと、ユーザーの継続利用率が25%向上します。モツモツの診断には、ガベージコレクションの頻度解析と並んでCPUおよびメモリのプロファイリングが必要です。

まとめ

  • モツモツ は、正常なパフォーマンスと交互に起こるアプリの閃発的な動作遅延です
  • 主な原因 — 頻繁なGCポーズ、UIスレッドでの同期操作、ページネーションなしのアダプターにおける大量のデータ
  • 診断には、ボトルネックを見つけるCPU Profilerと、GCの頻度と期間を解析するMemory Profilerが必要です
  • 解決策には、ページネーション(Paging 3)の導入、Roomを介したSQLクエリの最適化、重いタスクのWorkManagerへの移行が含まれます
  • 予防 — Benchmark Baseline Profiles、AOTコンパイル、ホットパスにおける割り当ての最小化

モバイル開発における“モツモツ”とは

モツモツは、ユーザーが主観的に遅いアプリケーションを記述するために使用する非正式な用語です。一定の遅延として現れるラグとは異なり、モツモツは不規則なフリーズから構成されます:アプリは数秒間完璧に動作したあと、突然1–3秒間“考えて”いるようになることがあります。

現象の技術的説明

プロファイリングの観点からは、モツモツは、ピーク遅延が100msを超える一連のフレームドロップ(jank)として現れます。FPSグラフでは、これは急激なドロップとして見えます:60→20→55→10フレーム/秒。一定的にFPSが低いラグとは異なり、モツモツには時間変動があります。

ユーザーの知覚

アプリがモツモツすると、ユーザーには遅くなった理由がわかりません。画面が滑らかにスクロールしていたかと思うと、突然一秒間停止することがあります。これは節になるもので、アプリへの信頼を損ないます。Googleによると、ローディングに3秒以上かかる場合、53%のユーザーがサイトまたはアプリを離れます。

アプリにおける突然の動作遅延の原因

モツモツの閃発的性質は、問題が一定的な過負荷ではなく、イベント驅動因子によって引き起こされていることを示しています。具体的なシナリオを見てみましょう。

オブジェクト割り当て時のGCポーズ

AndroidのARTランタイムでは、ガベージコレクションはすべてのアプリスレッドを停止します。コードが多くの一時オブジェクトを作成する場合、例えば毎回のonBindViewHolderコールで結合によって新しいStringを作成する場合、GCはより頻繁に実行されます。ポーズは、ヒープサイズとオブジェクトの生成世代によって、5–50msの間続することがあります。ユーザーはこれを突然の“考え深げ”として感知します。

UIスレッドでの同期SQLクエリ

AndroidのRoomやiOSのCore Dataは非同期クエリをサポートしていますが、開発者はよく簡単さのためにgetValue()を呼んだり、runBlockingを介してクエリを実行したりします。10,000行のテーブルに対するJOINを伴う重いSELECTは200–500msかかり、その間UIを完全にブロックします。

縮小なしの画像デコード

カメラ画像(12MP、4000x3000px)を縮小せずにロードすると、Bitmapへのデコードに200msまでかかります。画像を非同期的にロードしていても、制限のないスレッドプールを使用している場合、5–6のデコードを同時実行するとCPUに過負荷がかかり、移動的な動きの遅さが生じる可能性があります。

  • Android — ループ内での文字列結合、ホットパスでのオブジェクト生成、inSampleSizeなしのBitmap
  • iOS — 多くのオブジェクトを含むオートリリースプール、縮小なしのimageWithContentsOfFile、同期URLSession
  • クロスプラットフォーム — UIスレッドでのJSON解析、サーバー応答を待つ間のメインスレッドでのデータ読み込み

AndroidおよびiOSでのフリーズの診断方法

閃発的な動作遅延の診断は、一定的なラグの診断よりも難しいです。なぜなら、問題が毎回の実行で再現するとは限らないからです。長期的な統計データの収集が必要です。

GCイベント記録付きMemory Profiler

Android Studio Memory Profilerは、メモリ使用量だけでなく、GCイベント(頻度、タイプ(Concurrent、Full)、期間)も表示します。アイドル状態で5秒に1回以上GCが発生する場合から、これは過剰な割り当てのサインです。モツモツの瞬間にヒープダンプを取ると、どのオブジェクトがメモリを占用しているかがわかります。

Allocation Tracking付きXcode Instruments

iOSでは、InstrumentsのAllocationsテンプレートを使用して、オブジェクトの作成と解放をトレースします。Generationsを有効にすると、アクションの間にヒープのスナップショットを取り、メモリに残っているオブジェクトを確認できます。解放されない永続オブジェクトは、メモリの蓄積とその後のポーズの原因となります。

AndroidのJankStats API

JankStatsは、リアルタイムでフレームドロップメトリクスを収集するAndroidライブラリです。各jankを現在のシナリオ(例えば「リストスクロール」「画面開封」)に結びつけることで、どのアクションがモツモツを引き起こすのかを特定できます。

AndroidでJankStatsを組み込んでフリーズをトレースする例:

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var jankStats: JankStats

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        jankStats = JankStats.create(this.window.decorView) { frameData ->
            if (frameData.isJank()) {
                Log.w("Jank", "Duration=${frameData.durationMs}ms")
            }
        }
    }
}

動作が遅いアプリを改善する方法

モツモツを解消するには、各原因に対して目的を持った取り組みが必要です。万能の解決策はありません。診断には、個々のパフォーマンスプロファイルの解析が必要です。

Paging 3によるページネーションの導入

リストに1000+の項目があり、すべてを一度にロードする場合、それは確実にモツモツを引き起こします。AndroidのPaging 3やiOSのNSFetchedResultsControllerは、ユーザーがスクロールするにつれデータを分割って読み込みます。ユーザーには最初の10–20項目のみが表示され、残りはバックグラウンドで読み込まれます。

SQLクエリとインデックスの最適化

Roomは、Android StudioのInspection Toolを介してクエリのプロファイリングを可能にします:実行時間、戻された行数、クエリプランが表示されます。WHEREやORDER BY列にインデックスを追加すると、クエリ時間を300msから5msに減らせます。iOSでは、InstrumentsのCore Data Profilerが同様の検査を行います。

WorkManagerへのタスクの移行

バックグラウンド同期、ファイルダウンロード、データ処理—これらはすべてWorkManager(Android)またはBackground Tasks(iOS)を介して実行すべきです。同期がUIスレッドで実行される場合、実行中にアプリがモツモツします。WorkManagerは、バッテリーおよびネットワークの状態を考慮した上で、バックグラウンドスレッドでの実行を保証します。

AndroidでWorkManagerを介したバックグラウンド同期の例:

kotlin
class SyncWorker(context: Context, params: WorkerParameters)
    : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return try {
            Log.d("シンク", "バックグラウンドスレッドでデータをシンク中")
            syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

開発階階でのモツモツ予防

効率的なメモリおよびスレッド管理の原則に従うことで、コーディング階階でモツモツを予防できます。

AOTコンパイルのためのBaseline Profiles

Baseline Profilesは、AndroidがJITではなく事前にコンパイル(AOT)するクラスとメソッドのリストです。プロファイルがない場合、新しい画面は初回開いた時にコンパイルされ、100–500msの遅延が生じます。主な画面のBaseline Profileを作成し、baseline-profile-gradle-pluginを介してGradleで生成を有効にします。

ホットパスでの割り当ての最小化

ホットパスは、毎フレームで実行されるコードです:onBindViewHolder、draw、layoutSubviews。これらのメソッドでオブジェクトを作成するのを避けます:オブジェクトプールを使用し、結合の代わりにStringBuilderを使用し、フォーマットされた文字列とフォーマッターをキャッシュします。余計な割り当ては次のGCを近づけます。

CIでのBaseline Profilesによるプロファイリング

CIパイプラインに、リストスクロールと画面開封のシナリオを含むMacrobenchmarkを追加します。スレッショルドを設定します:フレーム時間の99パーセンタイルは16msを超えないものとします。この値を超えた場合、ビルドは最適化が完了するまで拒否されます。

  • Android — Baseline Profiles、Macrobenchmark、JankStats、penaltyDeath付きStrictMode
  • iOS — MetricKit、os_signpost、XCTMetric、DebugスキームのMain Thread Checker
  • 一般的なアプローチ — 定期的なプロファイリング、ホットパスでの割り当てに注目したコードレビュー

よくある質問

モツモツと通常のラグの違いは何ですか?

ラグは一定的な遅延です(例えば、毎回のタップで200ms)。モツモツは閃発的です:アプリは正常に動作し、突然1–3秒間遅くなり、また正常に戻ります。原因は、GCポーズやデータベースへの同期クエリなどのイベント驅動因子です。

AndroidでGCポーズの頻度を測るにはどうすればいいですか?

Android StudioのMemory Profilerを使用します:Memoryタブは、期間付きのGCイベントを表示します。プロダクション監視には、カスタムトレース付きのFirebase Performance Monitoringを組み込みます。iOSでは、Malloc Debugを有効にし、Instrumentsで割り当てのジェネレーションをマークします。

ネットワークリクエストはモツモツの原因になりますか?

間接的には—はい。サーバーからの応答が遅れ、UIが同期的に待っている場合、アプリはフリーズします。リクエストが非同期であっても、応答処理がUIスレッドで行われる場合もモツモツが生じます。解決策は、コルーチンと進捗インジケーターを使用した非同期処理です。

Kotlin Multiplatformはパフォーマンスにどのように影響しますか?

正しく使用されない場合、KMPは相互運用性のために過剰なラッパーオブジェクトを生成する可能性があります。iOSでは、これにより割り当ての頻度が増え、その結果ARCポーズが増えます。@ObjCNameを使用し、expect/actualを最適化し、UIのホットパスからの共有コードへの頻繁なコールを避けます。

Androidでヒープサイズを増やすと効果がありますか?

android:largeHeap=”true”によるヒープの拡張は、GCを遅らせますが、割り当ての原因を解消しません。GCが実際に実行されるとき、より多くのオブジェクトを巡る必要があるため、ポーズはより長くなります。解決策は、ヒープを拡張することではなく、割り当ての数を減らすことです。

まとめ

  • モツモツ は、イベント驅動因子(GCポーズ、同期クエリ、画像デコード)により引き起こされる閃発的なアプリ動作遅延です
  • 診断には、Memory Profiler、AndroidのJankStats、iOSのInstrumentsのAllocation Trackingが必要です
  • 主な原因 — 頻繁なGCポーズ、ページネーションの不備、非最適なSQLクエリ、UIスレッドでの同期処理
  • 解決策 — Paging 3、WorkManager、DBインデックスの最適化、画像の縮小、割り当ての最小化
  • 予防 — Baseline Profiles、Macrobenchmark、StrictMode、ホットパスをチェックするコードレビュー
  • ツール — プロダクションのモツモツ監視のためのJankStats、Firebase Performance、MetricKit
  • 推奨事項:99パーセンタイルのフレーム時間16msの制限値で、CIで定期的にMacrobenchmarkを実行しましょう

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください