2014年から個人開発でアプリを出し続けてきて、累計5,000万ダウンロードに到達した中で、起動時間とメモリのバランスは AdMob 収益と直結する数字になります。半端に重いアプリは、最初の広告が表示される前にユーザーが戻るボタンを押してしまうからです。
ここ2ヶ月、Rork で組み直した壁紙アプリと、癒し系の小さな AI アプリで、Hermes を有効にしたまま本番リリースを続けてきました。チュートリアル的な記事は世の中に多くあるのですが、「実際に2ヶ月運用したらどうなるのか」という温度感は意外と見つかりません。手元の実測ログを残しておきます。
計測対象と環境
対象としたのは、運用中の6本のアプリのうち、Rork のテンプレートから組み直した3本です。すべて Expo SDK 53 ベースで、Hermes は app.json の jsEngine: "hermes" でデフォルト有効にしてあります。比較対象としては、移行前の JSC(JavaScriptCore)時代に Firebase Performance Monitoring と Crashlytics で取った数字を、そのまま手元に残してあります。
計測機種は、私の手元の iPhone 13・iPhone 15 Pro・Pixel 7・iPhone SE 第2世代の4機種です。古い機種を残している理由は、累計5,000万 DL のうち4割以上が3年以上前の機種からのインストールという事実があるためで、新機種だけで判断すると現場の感覚を見誤ります。計測スクリプトは Expo の expo-application で取得した起動時刻と、react-native-performance の Mark を組み合わせた自前の簡易ロガーで、1機種につき1日10セッション、計28日分の中央値を採用しました。
初回起動:38% 短縮の中身
JSC 時代の平均初回起動が iPhone SE 第2世代で 2.4 秒、iPhone 15 Pro で 0.9 秒でした。Hermes に切り替えてから2週間慣らした時点では、それぞれ 1.6 秒・0.55 秒まで落ちました。SE で 33%、15 Pro で 39%、平均で 38% の短縮です。
数字だけ見ると気持ちのよい改善なのですが、内訳を見ると単純ではありませんでした。Hermes のバイトコードプリコンパイルが効くのは「ネイティブブリッジに到達した直後から最初の render() まで」の区間で、私のアプリだと全体時間の 6 割程度しか覆っていません。残り 4 割は AdMob SDK の起動、Firebase の初期化、RevenueCat の課金復元で、ここは Hermes では縮みません。
つまり、Hermes 単体での効果は 5〜6 割の区間で 50% 前後の短縮、と理解しておくと期待値が合います。広告 SDK や課金 SDK を遅延初期化する別の工夫と組み合わせて、ようやく体感の差になります。これは個人開発で AdMob を主収益源にしている立場だと、毎日眺めている数字に直結する話で、起動を 1 秒短縮すると、私のアプリ群では初回広告到達率が 4〜6 ポイント上がる感覚があります。
メモリ:Pixel 7 で 18% 減
メモリは Android のほうが効きが大きく出ました。Pixel 7 のホーム画面復帰時の RSS が、JSC 時代の平均 142 MB から 116 MB まで落ちて、約 18% 減です。iPhone は 8〜10% 減で、Android ほどドラスティックではありませんでした。
これは Hermes のヒープ管理が、Android の ART と相性がよく、世代別 GC の動きが噛み合うためだと理解しています。実害として、私のアプリの場合は、Pixel 7 の「メモリ不足でバックグラウンド復帰時に再起動」というクラッシュが、2ヶ月で 23 件から 3 件まで減りました。Firebase Crashlytics の Issue として残っていたものが、Hermes 切替後に静かに自然消滅した形です。
加えて、低スペック Android 機(Pixel 3a・Galaxy A シリーズの古い世代)でのバックグラウンド復帰時の白画面率も、JSC 時代の 11% から 4% まで下がりました。OS による強制終了を防ぐ余裕が増えた、という表現が一番近い感覚です。
クラッシュフリー率と注意点
クラッシュフリー率は、移行前の 99.4% から 99.7% まで上がりました。これは Hermes そのものが安定したというより、メモリ起因のクラッシュが減った副作用が大きいです。
ただ、注意点も実体験として残っています。Hermes はソースマップが JSC とは別系統で、Sentry のスタックトレースが最初の数日「謎の関数名」だらけになりました。hermesc で生成したシンボルマップをアップロードする手順を Step 0 として入れておかないと、本番のデバッグが事実上止まります。私の場合、最初の本番クラッシュを追うのに半日溶かしました。
もうひとつ、Intl 周りで挙動が JSC と微妙に違いました。toLocaleString("ja-JP", ...) の出力が、古いバージョンの Hermes だと数値の桁区切りカンマが落ちることがあり、AdMob の eCPM 表示や課金額の表示で軽い表示崩れが出ました。Expo SDK 53 同梱の Hermes で解消しているのですが、SDK 51 以前のプロジェクトを混在させている方は要注意です。
2ヶ月続けて見えたこと
2ヶ月運用してみての結論として、Hermes は「速くなる魔法のスイッチ」ではなく「メモリと起動を整える基礎工事」だと感じています。広告や課金 SDK の起動順序、画像の遅延ロード、Splash の出し方など、別レイヤーの工夫と組み合わせて、ようやくユーザーが体感できる差になります。
それでも、メモリ起因のクラッシュが Android で減ったことは、累計5,000万 DL のうち6割を占める Android ユーザーの定着に静かに効きました。Day 1 リテンションが該当アプリで 2 ポイント上がり、これは AdMob の月間 eCPM 換算で 5〜8% の上乗せに相当します。
次に取り組むのは、Hermes のバイトコードを EAS Update の OTA に乗せたときのサイズ最適化です。metro.config.js で transformer.minifierPath を見直して、配信サイズをさらに 15% 圧縮できないかを試そうとしています。結果は1ヶ月後にまた残します。同じように Rork と Hermes を本番で回している方の参考になれば嬉しいです。