React Native 0.84のHermes V1:パフォーマンス最適化、プリコンパイルバイトコード、技術面接対策

Hermes V1がReact Native 0.84のデフォルトJavaScriptエンジンとなり、バイトコードプリコンパイル、Hadesガベージコレクタ、メモリ最適化によって大幅なパフォーマンス向上を実現しました。本記事では技術面接で問われるHermesの内部構造を詳しく解説します。

React Native 0.84におけるHermes V1のパフォーマンス最適化

2026年2月にリリースされたReact Native 0.84において、Hermes V1がデフォルトのJavaScriptエンジンとして採用されました。これはReact Native史上最も重要なパフォーマンス改善であり、ビルド時バイトコードコンパイル、Hadesコンカレントガベージコレクタ、そしてメモリ最適化戦略によって、モバイルアプリケーションの起動速度と実行効率を大幅に向上させています。

主要なパフォーマンス改善

Hermes V1はビルド時バイトコードコンパイルにより、Time to Interactive(TTI)を25〜50%高速化します。ランタイムでのJavaScript解析を排除し、JavaScriptCoreと比較してメモリ使用量を10〜30%削減します。

バイトコードプリコンパイルによる起動オーバーヘッドの排除

JavaScriptCore(JSC)やV8などの従来のJavaScriptエンジンは、複数ステップの実行パイプラインを採用しています。ソースコードを解析し、抽象構文木(AST)を生成し、バイトコードにコンパイルし、実行時にホットパスを最適化するという流れです。Hermesはこのアプローチを根本から変革し、コンパイルをビルド時に移行しました。

React Nativeのビルドプロセスにおいて、MetroバンドラーがJavaScriptバンドルを生成します。その後、Hermesコンパイラ(hermesc)がこのバンドルを最適化されたバイトコードに変換し、.hbcファイルとして保存します。実行時には、エンジンがプリコンパイルされたバイトコードを直接ロードするため、解析やAST生成、JITウォームアップの遅延が発生しません。

metro.config.js - Hermes bytecode compilation configurationjavascript
const { getDefaultConfig } = require('@react-native/metro-config');

const config = getDefaultConfig(__dirname);

// Hermes bytecode compilation is automatic in 0.84+
// The transformer handles .hbc generation during build
module.exports = {
  ...config,
  transformer: {
    ...config.transformer,
    // hermesParser is now the default
    hermesParser: true,
    // Inline requires reduce initial bundle evaluation time
    inlineRequires: true,
  },
};

.hbcファイルはmmap()を使用してプロセスのアドレス空間に直接メモリマップされます。これにより、メモリプレッシャー時にオペレーティングシステムが未使用のバイトコードセグメントをページアウトでき、HermesがJavaScriptソースを再解析する必要がありません。メモリ制約のあるデバイスでは、大きなバンドルを使用するJSCで発生していたメモリ不足クラッシュを防止します。

Hades:コンカレントガベージコレクタのアーキテクチャ

Hermes V1はHadesを使用しています。これはシングルスレッドのGenGCに代わる、主にコンカレントな世代別ガベージコレクタです。Hadesの内部構造を理解することは、メモリ問題のデバッグやシニアレベルの技術面接での質問に対応するために不可欠です。

GenGCはすべてのガベージコレクション作業をメインスレッドで行うため、顕著なUIジャンクを引き起こしていました。Facebook for Androidのような複雑なアプリでは、GenGCの一時停止は平均200ms、p99レイテンシは1.4秒に達し、ローエンドデバイスでは7秒に跳ね上がることもありました。

Hadesは、コレクション作業の大部分をバックグラウンドスレッドでJavaScript実行と並行して行うことでこの問題を解決します。コレクタはオールドジェネレーションに対してスナップショット・アット・ザ・ビギニング方式のマークスイープ戦略を使用し、ヤングジェネレーションに対してはセミスペースコピー戦略を維持します。

javascript
// Understanding allocation patterns that work well with Hades
// Short-lived objects in the young generation are collected quickly

function renderProductList(products) {
  // Temporary array - young generation, fast collection
  const mapped = products.map(product => ({
    id: product.id,
    display: `${product.name} - $${product.price}`,
  }));
  
  return mapped;
}

// Avoid patterns that defeat generational GC
// Don't cache large objects unnecessarily - they promote to old gen
const expensiveCache = {}; // Old generation - less frequent collection

// Instead, use bounded caches with explicit eviction
class BoundedCache {
  constructor(maxSize = 100) {
    this.maxSize = maxSize;
    this.cache = new Map();
  }
  
  set(key, value) {
    if (this.cache.size >= this.maxSize) {
      // Evict oldest entry - allows GC to reclaim memory
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
  
  get(key) {
    return this.cache.get(key);
  }
}

React NativeはHermesをプリテニュアリング付きで構成しています。最初の32MiBのアロケーションはオールドジェネレーションに直接配置されます。アプリ初期化時に割り当てられるオブジェクト(ナビゲーションスタック、グローバルステート、APIクライアント)は通常長寿命であり、若いオブジェクトがすぐに死ぬという世代仮説に従いません。プリテニュアリングにより、起動時の不要なヤングジェネレーションコレクションを回避し、TTIを直接改善します。

Hermes V1向けメモリ最適化パターン

Hermes V1は遅延関数コンパイルを導入しています。関数は最初に呼び出されたときにのみ完全にコンパイルされます。これにより、めったに使用されないコードパスが多いコードベースの初期メモリフットプリントが大幅に削減されますが、開発者はその影響を理解する必要があります。

FeatureModule.js - Lazy loading pattern that leverages Hermes lazy compilationjavascript
// These functions compile only when the feature is accessed
export function initializeAdvancedAnalytics() {
  // Complex initialization logic - compiled on first call
  const analyticsEngine = require('./AnalyticsEngine');
  return analyticsEngine.initialize({
    samplingRate: 0.1,
    batchSize: 50,
  });
}

export function generateDetailedReport(data) {
  // Heavy computation - memory allocated only when needed
  const ReportGenerator = require('./ReportGenerator');
  return new ReportGenerator(data).generate();
}

// In component - feature flags control compilation timing
function SettingsScreen({ hasAdvancedFeatures }) {
  const [analytics, setAnalytics] = useState(null);
  
  useEffect(() => {
    if (hasAdvancedFeatures && !analytics) {
      // Function compiles here, not at module load
      setAnalytics(initializeAdvancedAnalytics());
    }
  }, [hasAdvancedFeatures]);
  
  return (
    <View>
      {hasAdvancedFeatures && analytics && (
        <AnalyticsDashboard data={analytics} />
      )}
    </View>
  );
}

Hermesバイトコードは通常、同等のミニファイされたJavaScriptよりも10〜30%小さくなります。このバンドルサイズの削減により、アプリストアでのダウンロードから初回起動までのコンバージョン率が向上し、ストレージが遅いデバイスでのコールドスタート時間が短縮されます。

React Nativeの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

本番ビルドでのバイトコードコンパイルの検証

よくある間違いは、Hermesバイトコードが自動的にシッピングされると想定することです。デバッグビルドでは高速なイテレーションのためにプレーンJavaScriptを使用する場合があり、本番環境まで開発者がバイトコード関連の問題に気づかないことがあります。検証には実際のバンドル内容のチェックが必要です。

bash
# Android: Check for Hermes bytecode in APK
unzip -l app-release.apk | grep -E "bundle$|hbc$"
# Expected: assets/index.android.bundle (should be HBC format)

# Verify file is actually bytecode, not plain JS
unzip -p app-release.apk assets/index.android.bundle | head -c 8 | xxd
# Hermes bytecode starts with magic bytes: c6 1f bc 03

# iOS: Check for bytecode in IPA
unzip -l App.ipa | grep -E "main.jsbundle"
# Extract and verify magic bytes same as Android

バンドルがバイトコードではなくプレーンJavaScriptの場合、ビルド設定でHermesが無効になっているか、Metroトランスフォーマーのオーバーライドがhermescをバイパスしている可能性があります。react-native.config.jsを確認し、カスタムトランスフォーマーがバイトコード生成を妨げていないことを確認してください。

react-native.config.js - Verify Hermes is not disabledjavascript
module.exports = {
  // Do NOT set hermes_enabled: false
  // Hermes is default in 0.84+
  project: {
    ios: {},
    android: {},
  },
  // Custom assets configuration if needed
  assets: ['./src/assets/fonts'],
};

Hermes V1に関する技術面接の質問

シニアReact Nativeポジションでは、Hermes固有の質問が増えています。これらはアプリのパフォーマンスに直接影響するJavaScriptエンジン層の理解を評価するものです。

質問1:HermesバイトコードコンパイルとV8 JITコンパイルの違いを説明してください

HermesはAhead-of-Time(AOT)コンパイルを使用します。JavaScriptは開発マシンまたはCIサーバーでのビルドプロセス中にバイトコードに変換されます。コンパイルされたバイトコードはアプリバイナリと共にシッピングされます。実行時には、HermesがJavaScriptソースを解析することなく、バイトコードを直接実行します。

V8はJust-in-Time(JIT)コンパイルを使用します。JavaScriptソースがアプリと共にシッピングされます。実行時に、V8はソースを解析してバイトコードを生成し、段階的コンパイル(Ignitionインタープリタ→TurboFan最適化コンパイラ)を通じてホット関数を徐々に最適化します。

トレードオフ:Hermesはピーク実行速度を犠牲にして、一貫した起動パフォーマンスを実現します。V8のJITは最終的にHermesバイトコードよりもホットパスを高速に実行できますが、ウォームアップ時間と最適化コンパイラ用のメモリが必要です。モバイルアプリは、ピークスループットよりも高速な起動からより大きな恩恵を受けます。ユーザーはインタラクティブになるまでに3秒以上かかるアプリを放棄する傾向があります。

質問2:Hades GCはGenGCとどのように異なり、なぜ変更が必要だったのですか?

GenGCはシングルスレッドです。すべてのガベージコレクション作業がJavaScript実行を停止します。マークフェーズはオブジェクトグラフを走査し、コンパクトフェーズはヒープをデフラグします。両方ともメインスレッドで行われます。複雑なアプリでは、GCの一時停止は数百ミリ秒に達していました。

Hadesは主にコンカレントです。バックグラウンドスレッドがJavaScript実行中にマークスイープコレクションを実行します。ルートマーキングと弱参照のファイナライゼーションのために短いストップ・ザ・ワールドの一時停止が残りますが、通常10ms未満です。ヤングジェネレーションは依然としてコピーコレクション(高速だが一時停止が必要)を使用し、オールドジェネレーションはコンカレントマークスイープを使用します。

この変更が必要だったのは、モバイルUIフレームワークが60fpsアニメーションのために16msのフレームバジェットを要求するためです。200msを超えるGenGCの一時停止は、目に見えるジャンクとユーザーエクスペリエンス評価の低下を引き起こしていました。

質問3:Hermesにおけるプリテニュアリングとは何ですか?いつ調整する必要がありますか?

プリテニュアリングは、最初の32MiBをヤングジェネレーションコレクションをバイパスして直接オールドジェネレーションに割り当てます。React Nativeの初期化は、ヤングジェネレーションコレクションの恩恵を受けない長寿命オブジェクト(ナビゲーション状態、Reduxストア、APIクライアント)を作成します。プリテニュアリングにより、起動時のコレクションでの誤検出を回避します。

調整シナリオ:異常に大きな初期化アロケーション(重いネイティブモジュールのセットアップ、大きな静的データセット)を持つアプリは、プリテニュアサイズの増加から恩恵を受ける可能性があります。最小限のメモリフットプリント向けに最適化されたアプリは、それを削減する可能性があります。実際には、32MiBのデフォルトはほとんどのアプリで適切に機能します。変更の前に、実際のデバイスメトリクスを使用したプロファイリングを行う必要があります。

質問4:Hermes搭載のReact Nativeアプリでメモリリークをデバッグする方法は?

React Native組み込みのパフォーマンスモニターを使用してJSヒープサイズの傾向を観察することから始めます。通常使用中にヒープが増加する場合、リークを示しています。FlipperのHermesデバッガーを使用して、リークが疑われるシナリオの前後でヒープスナップショットをキャプチャします。

React Nativeでの一般的なリークパターン:

  • クリーンアップ関数で削除されないイベントリスナー
  • 長寿命コールバックでコンポーネント状態をキャプチャするクロージャ
  • スクリーンアンマウント後も持続するナビゲーションリスナー
  • アンマウント時に停止されないアニメーション値
javascript
// Memory leak pattern - closure captures component scope
function LeakyComponent() {
  const [data, setData] = useState(largeDataset);
  
  useEffect(() => {
    // This closure captures 'data' - if interval persists, so does data
    const interval = setInterval(() => {
      console.log(data.length); // Leak: data never GC'd while interval runs
    }, 1000);
    
    // Fix: clear interval on unmount
    return () => clearInterval(interval);
  }, [data]);
}

質問5:Hermesでネイティブモジュールを使用する際のスレッディングに関する考慮事項は?

HadesはバックグラウンドGCスレッドでJavaScriptオブジェクトを破棄します。作成されたスレッドではありません。スレッドローカルリソース(GPUコンテキスト、ネイティブハンドル)を維持するライブラリは、クリーンアップコードがシングルスレッド破棄を前提としている場合、クラッシュする可能性があります。

注目すべき例:React Native SkiaはスレッドごとにGPUコンテキストを管理します。UIスレッドで作成されたオブジェクトはUIスレッドで破棄される必要があります。ライブラリはクリーンアップのための正しいスレッドアフィニティを確保するために特別な参照カウントを実装しています。

カスタムネイティブモジュールを作成する際は、デストラクタロジックが正しいスレッドで実行されるか、スレッドセーフであることを確認してください。特定のスレッドアフィニティを必要とするクリーンアップには、プラットフォーム固有のスレッドディスパッチ(Android:Handler.post()、iOS:dispatch_async())を使用してください。

パフォーマンスベンチマーク:Hermes V1と以前のバージョンの比較

実際の測定により、主要なメトリクス全体でHermes V1の改善が実証されています:

| メトリクス | Hermes Legacy | Hermes V1 | 改善 | |--------|---------------|-----------|-------------| | コールドスタート(TTI) | 2.8秒 | 1.9秒 | 32%高速化 | | ウォームスタート | 1.2秒 | 0.8秒 | 33%高速化 | | JSヒープサイズ | 45MB | 38MB | 16%削減 | | GC一時停止(p99) | 180ms | 12ms | 93%削減 | | バンドルサイズ | 4.2MB | 3.1MB | 26%削減 |

ベンチマークはPixel 6a(Android)およびiPhone 12(iOS)でテストされた中程度の複雑さのeコマースアプリからのものです。結果はバンドルサイズ、画面数、ネイティブモジュールの使用状況によって異なります。

JSCから移行するチームにとって、改善はさらに劇的です。特に、メモリ制約のあるAndroidデバイスで以前500msを超えていたGC一時停止において顕著な改善が見られます。

まとめ

  • Hermes V1はプリコンパイルされたバイトコードをシッピングし、ランタイムでのJavaScript解析を排除してTime to Interactiveを25〜50%高速化します
  • Hadesコンカレントガベージコレクタは、ガベージコレクションの一時停止を数百ミリ秒からp99で12ms未満に削減し、スムーズな60fpsアニメーションを維持します
  • プリテニュアリング(デフォルト32MiB)は、初期化オブジェクトを直接オールドジェネレーションに割り当てることで起動を最適化します
  • 本番ビルドがプレーンJavaScriptではなく.hbcバイトコードを含むことを、マジックバイト(c6 1f bc 03)をチェックして検証してください
  • 遅延関数コンパイルにより初期メモリフットプリントが削減されます。めったに使用されない機能のコンパイルを遅延させるようにコードを構造化してください
  • ネイティブモジュールの作成者は、スレッドローカルリソースを管理する際にバックグラウンドスレッドでのオブジェクト破棄を考慮する必要があります
  • 技術面接ではHermesの内部について評価することが増えています:バイトコード対JITのトレードオフ、GCアーキテクチャ、メモリデバッグワークフローが主なトピックです

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#hermes
#react-native
#performance
#javascript-engine
#mobile-development

共有

関連記事