V8 Isolate(アイソレート)は、JavaScriptエンジンV8の状態を丸ごと分けた実行単位です。Isolateごとにヒープとガベージコレクタが独立しているため、1つのOSプロセスの中に、互いのオブジェクトに触れられない実行環境を多数並べられます。Cloudflare Workersがコンテナを使わずに多数の顧客コードを同じマシンで動かせるのは、この仕組みによります。本記事では、V8公式APIの定義から始めて、Contextとの違い、Node.jsでの3つの分離手段、起動時間とメモリの実測、Cloudflare Workersの制約、Isolateだけでは足りない防御までを順に説明します。コード例と計測値は Homebrew版の node v26.5.0(V8 14.6)、isolated-vm 7.0.2、workerd 1.20260730.1 で2026年11月9日に実行したものです。
まとめ:V8 Isolateは状態を分けた実行単位、分離の強さはV8と周囲の防御次第
V8 Isolateは「V8エンジンの独立した1インスタンス」です。Isolateをまたいでオブジェクトを渡すことはできず、ヒープもガベージコレクションも別々に動きます。その内側にグローバル環境を表すContextが1つ以上入ります。Node.jsの vm.createContext() が作るのはContextで、Isolateではありません。
軽さは手元のIntel Macで確かめました。Isolate+Contextの生成と式の評価は中央値6.48ミリ秒で、Node.js子プロセスの起動から終了までの234.7ミリ秒の約36分の1でした。計測区間が異なるため、純粋な起動時間の比ではありません。1個あたりのメモリ増分は1.19MBで、Node.jsプロセス(32MB)の約27分の1でした。一方で、Isolateの境界はV8というソフトウェアの正しさに依存します。Cloudflareはプロセス分離・seccomp・24時間未満のパッチ適用・時刻の固定を重ねており、自前で信頼できないコードを動かすなら同じ種類の防御を自分で用意します。
V8 Isolateの定義:V8公式APIの記述とスレッドの制約
V8のC++ APIリファレンスは、v8::Isolate を次のように定義しています。
Isolate represents an isolated instance of the V8 engine. V8 isolates have completely separate states. Objects from one isolate must not be used in other isolates.
「完全に分かれた状態」とは、JavaScriptのオブジェクトを置くヒープ、ガベージコレクタ、組み込みオブジェクトの一式がIsolateごとに別という意味です。あるIsolateで作った配列の参照を別のIsolateへ持ち込むことはAPI上禁止されています。値を渡すときは、構造化複製のようにコピーするか、埋め込み側(エンベッダー)が用意した仲介の仕組みを通します。
同じリファレンスには、スレッドとの関係も書かれています。エンベッダーは複数のIsolateを作って複数スレッドで並列に動かせますが、An isolate can be entered by at most one thread at any given time. の通り、1つのIsolateに同時に入れるスレッドは1本だけです。複数スレッドで同じIsolateを使うなら Locker で排他します。並列性はIsolateの数で稼ぐ設計です。
埋め込み側から見ると、Isolateは明示的に作って破棄する対象です。V8公式サンプルの hello-world.cc は、Isolate::New で作り、Isolate::Scope で入り、HandleScope を積んでから Context::New と Context::Scope で実行環境に入ります。あとは Script::Compile と Run で実行し、最後に isolate->Dispose() で破棄します。Node.jsもChromeもCloudflare Workersも、V8をこの順序で使うエンベッダーです。
IsolateとContextの違い:ヒープの共有範囲とvmモジュールの落とし穴
v8::Context は、V8のリファレンスで A sandboxed execution context with its own set of built-in objects and functions. と説明されています。Contextは独自の globalThis、Object、Array などの組み込み一式を持つグローバル環境です。ただしContextはIsolateの中に作られ、同じIsolateの中のContext同士はヒープを共有します。ブラウザでiframeごとに別の window があっても、同一オリジンなら互いのオブジェクトを参照できるのはこの構造によります。
| 単位 | ヒープ | グローバル環境 | 並列実行 | Node.jsでの作り方 |
|---|---|---|---|---|
| Context | Isolate内で共有 | 独立 | 不可(同じスレッド) | vm.createContext() |
| Isolate | 独立 | 中のContextごと | Isolate単位で可 | Worker Threads・isolated-vm |
| OSプロセス | 独立 | 独立 | 可 | child_process・cluster |
この違いが実害になるのが、vm モジュールを「サンドボックス」だと誤解する場面です。空のオブジェクトからContextを作っても、Context内のコードはコンストラクタをたどってホスト側の Function に届き、そこから process を取り出せます。
import vm from 'node:vm';
const ctx = vm.createContext({});
const p = vm.runInContext(
"this.constructor.constructor('return process')()",
ctx
);
console.log(p.pid === process.pid); // true
console.log(typeof p.getBuiltinModule); // 'function'
隔離したはずのContextからホストの process が返り、process.getBuiltinModule() 経由で fs や child_process も読み込めます。Node.js公式ドキュメントも vm の項で The node:vm module is not a security mechanism. Do not use it to run untrusted code. と警告しています。vm はテンプレート評価のように、信頼できるコードのグローバルを分けたいときの道具です。
Node.jsでコードを分離する手段:vm・Worker Threads・isolated-vmの比較
Node.jsのメインスレッドは1つのIsolateで動きます。Node.jsそのものの仕組みはNode.jsの仕組みと版の選び方に譲り、ここでは前章のvmに加えてIsolateを追加する2つの手段を取り上げます。
Worker Threads:resourceLimitsによるヒープ上限の設定
node:worker_threads の Worker は、別スレッドに新しいJSエンジンのインスタンスを立ち上げます。Worker内では require も fs も使えるので、隔離ではなく並列計算のための仕組みです。それでも resourceLimits でヒープの上限を切れる点は、暴走したコードからメインスレッドを守るのに役立ちます。
import { Worker } from 'node:worker_threads';
const w = new Worker(
'const a = []; while (true) { a.push(new Array(1e5).fill(1)); }',
{ eval: true, resourceLimits: { maxOldGenerationSizeMb: 16 } }
);
w.on('error', (e) => console.log(e.code)); // ERR_WORKER_OUT_OF_MEMORY
w.on('exit', (code) => console.log(code)); // 1
Workerは ERR_WORKER_OUT_OF_MEMORY で終了し、メインスレッドは動き続けました。ただし公式ドキュメントのとおり、この上限はJSエンジンのヒープだけに効き、ArrayBuffer などの外部データは対象外です。プロセス全体がメモリ不足になれば、上限を設定していてもプロセスごと落ちます。
isolated-vm:転送可能な値とmemoryLimit・timeoutによる実行制限
isolated-vmは、V8の Isolate インターフェースをNode.jsから直接使えるようにするネイティブアドオンです。作ったIsolateの中にはNode.jsのAPIが一切ありません。READMEの指示どおり node --no-node-snapshot で起動して次のコードを実行すると、コメントの結果になりました。
import ivm from 'isolated-vm';
const iso = new ivm.Isolate({ memoryLimit: 8 }); // MB
const ctx = await iso.createContext();
console.log(await ctx.eval('typeof process')); // 'undefined'
await ctx.global.set('copied', { a: 1 }, { copy: true });
console.log(await ctx.eval('copied.a')); // 1
try {
await ctx.global.set('raw', { a: 1 });
} catch (e) {
console.log(e.message); // A non-transferable value was passed
}
await ctx.global.set('hostFn', () => 'from host');
console.log(await ctx.eval('hostFn()')); // 'from host'
try {
await ctx.eval('while (true) {}', { timeout: 50 });
} catch (e) {
console.log(e.message); // Script execution timed out.
}
try {
await ctx.eval('const a = []; while (true) { a.push(new Array(1e5).fill(1)); }');
} catch (e) {
console.log(e.message); // Isolate was disposed during execution due to memory limit
}
console.log(iso.isDisposed); // true
この例の通常のオブジェクトリテラルは、copy: true などの転送方法を指定しなければ拒否されます。これがIsolateの「オブジェクトを持ち込めない」という性質の現れです。一方、関数は Callback に自動で変換され、隔離側から普通の関数として呼べました。関数の中身はホスト側で動くので、渡した関数がそのまま隔離の出口になります。渡す関数は、引数を検証して必要な処理だけをする小さなものに限ります。
メモリ超過のあとIsolateは使えなくなるので、実運用では実行のたびに作り直すか、破棄を検知して再生成します。memoryLimit については、READMEが A determined attacker could use 2-3 times this limit と書いています。悪意のあるコードでは、8MBと指定しても16〜24MB程度を消費し得ます。memoryLimitは厳密な上限ではないため、この範囲を保証された最大値とは扱わず、別プロセス側にもメモリ制限を設けてください。
導入前に確認すべき制約もあります。READMEはプロジェクトを maintenance mode と明記し、技術的に可能な限りサポートを続けるとしています。対応表はNode.js 22が5.xか4.x、24が6.xか5.x、26が7.xで、奇数版のNode.jsはサポートしていません。npmの最新版7.0.2(2026年10月30日公開)は engines が node >=24.0.0 です。Node.js 20以降では --no-node-snapshot の指定が求められます。
READMEのSECURITY節は、Reference や ExternalCopy などのインスタンスを信頼できないコードへ渡すと、それを足がかりにホスト側のIsolateへ戻られると警告しています。隔離側に渡すのは、copy: true で複製した値と、検証済みの小さな関数だけにします。
vm・Worker Threads・isolated-vmの選定基準:コードの信頼性とメモリ上限
| 観点 | vm | Worker Threads | isolated-vm |
|---|---|---|---|
| 分離の単位 | Context | Isolate+スレッド | Isolate |
| Node.js API | ホストに到達可能 | 使える | 無い |
| メモリ上限 | 無し | resourceLimits |
memoryLimit |
| 実行時間の上限 | timeout(同期のみ) |
terminate() |
timeout |
| 追加の依存 | 無し | 無し | ネイティブビルド |
| 向く用途 | 信頼済みコードの評価 | CPU負荷の並列化 | 第三者コードの実行 |
vm の timeout は既定で同期実行だけが対象で、microtaskMode: 'afterEvaluate' を指定するとマイクロタスクも時間制限に含まれます。判断の軸は、実行するコードを書いたのが誰かです。自社のコードを並列化したいならWorker Threads、利用者やLLMが書いたコードを動かすならisolated-vm、またはそれ以上に強い隔離を選びます。isolated-vmのREADME自身が、重要な基盤とは別のNode.jsプロセスで動かすよう勧めています。
起動時間とメモリの実測:Isolate・Worker・Node.jsプロセスの比較
Cloudflareの公式ドキュメントは、Isolateの起動はコンテナや仮想マシン上のNodeプロセスのおよそ100倍速く、起動時のメモリ消費は1桁少ないと説明しています。数値の出どころは2018年のCloudflareブログで、Isolateは5ミリ秒で起動し、コードを持たないNode.jsのLambdaが35MBを消費するのに対して、ランタイムを共有すると約3MBに下がると書かれています。同じ差を手元で確かめました。環境はIntel Core i9-9880H・macOS・node v26.5.0(他のワーカーが動く負荷のある状態)で、コンテナとマイクロVMは計測していません。
生成・実行時間:Isolateの生成・評価とNode.jsプロセスの起動・終了の比較
| 計測対象 | 中央値(5回) | 1回の試行数 |
|---|---|---|
| isolated-vmでIsolate+Contextを作り1式を評価 | 6.48ms | 100 |
| Workerを起動し最初のメッセージを受信 | 90.3ms | 20 |
node -e 1 の子プロセス起動から終了まで |
234.7ms | 20 |
Workerが90ミリ秒かかるのは、Isolateに加えてスレッドとNode.jsの実行環境(イベントループやモジュールローダー)を初期化するためです。2018年ブログの約100倍はNodeプロセスの起動との比較です。今回の約36倍は、Isolate+Contextの生成・式の評価とNode.js子プロセスの起動から終了までの比較であり、同一の計測条件ではありません。倍率はハードウェアやNode.jsの版で変わるので、桁の目安として読んでください。
メモリ:Isolate 1,000個で1個あたり1.19MB
公式ドキュメントの「1つのランタイムで数百から数千のIsolate」を確かめるため、1,000個のIsolateを同じプロセスに保持してRSS(物理メモリ使用量)の増分を測りました。
import ivm from 'isolated-vm';
const N = 1000;
const rss = () => process.memoryUsage().rss / 1048576;
const before = rss();
const kept = [];
for (let i = 0; i < N; i++) {
const iso = new ivm.Isolate({ memoryLimit: 8 });
const ctx = iso.createContextSync();
ctx.evalSync('globalThis.x = 1');
kept.push([iso, ctx]);
}
await new Promise((r) => setTimeout(r, 800));
console.log(((rss() - before) / N).toFixed(2), 'MB/isolate');
process.exit(0);
| 単位 | 1個あたりのRSS | 測り方 |
|---|---|---|
| Isolate+Context | 1.19MB | 1,000個を保持した増分 |
| Worker | 11.4MB | 50個を保持した増分 |
| Node.jsプロセス | 32MB | node -p の自己申告 |
上のコードは5回とも1.19MBで、同型のスクリプトを合わせた10回のうち1回だけ0.57MBでした。1,000個まで作っても1個あたりの生成時間は6.8〜7.0ミリ秒で、作るほど遅くなる傾向は見えません。1,000個の合計は約1.2GBです。公式の「数千個」は空のIsolateなら16GB級のマシンに収まりますが、これはコードもデータも持たないIsolateの値で、載せた分だけ増えます。
Isolate・コンテナ・マイクロVMの分離境界と選び方
コンテナより強い隔離が必要で、Isolateより重くてもよい場合の選択肢がマイクロVMです。AWS LambdaはFirecrackerというマイクロVMで関数ごとにカーネルまで分けています。仕組みはFirecracker(マイクロVM)の仕組み、Lambda側の対策はAWS Lambdaの仕組みとコールドスタート対策で解説しています。
| 方式 | 分離の境界 | 動かせるもの | 代表例 |
|---|---|---|---|
| V8 Isolate | V8のヒープ分離 | JavaScript・Wasm | Cloudflare Workers |
| コンテナ | カーネルの名前空間 | 任意のLinuxプロセス | Docker・Kubernetes(runc) |
| マイクロVM | ハイパーバイザー | 任意のLinuxプロセス | AWS Lambda(Firecracker) |
Isolateが使えないのは、ネイティブバイナリや任意の言語ランタイムを動かしたいときです。V8の中で動くのはJavaScriptとWebAssemblyだけなので、WebAssembly向けにビルドされていないネイティブ拡張や、特定のLinuxコマンドに依存する処理は載りません。中間の選択肢はKata ContainersとgVisor・runcの比較を参照してください。
Cloudflare WorkersでのIsolateの制約:再利用・128MB・起動
サーバー側でIsolateをマルチテナントの分離単位に使う代表例がCloudflare Workersです。Workers全体の料金や使い方はCloudflare Workersの対応言語・無料枠・料金にまとめているので、ここではIsolate由来の制約だけを挙げます。
グローバル変数の寿命:workerdでの再利用の確認
Workersのランタイムを公開したworkerdを、miniflare 4.20260730.0 でローカルに立てて確かめました。同じスクリプトをWorker aとbとして読み込み、グローバル変数のカウンタとCPUループ中の時計を見ます。
import { Miniflare } from 'miniflare';
const script = `
let n = 0;
export default {
async fetch(req) {
if (new URL(req.url).pathname === '/clock') {
const t0 = Date.now();
for (let i = 0; i < 3e8; i++) {}
return Response.json({ spinMs: Date.now() - t0 });
}
n++;
return Response.json({ n });
},
};`;
const base = { modules: true, script, compatibilityDate: '2026-08-01' };
const conf = {
workers: [
{ name: 'a', ...base },
{ name: 'b', ...base },
],
};
const mf = new Miniflare(conf);
const a = await mf.getWorker('a');
const b = await mf.getWorker('b');
const get = async (w, p) => (await w.fetch('http://x' + p)).json();
for (let i = 0; i < 3; i++) console.log('a', await get(a, '/')); // n: 1, 2, 3
console.log('b', await get(b, '/')); // n: 1
console.log(await get(a, '/clock')); // spinMs: 331
await mf.setOptions(conf); // 同じ設定で読み込み直す
console.log('a', await get(await mf.getWorker('a'), '/')); // n: 1
await mf.dispose();
Worker aのカウンタは1・2・3と増え、bは1から始まり、読み込み直すと1に戻りました。同じWorkerの複数リクエストが同じIsolateを再利用することはありますが、本番では同じIsolateへ届く保証はありません。Workerが異なればIsolateも分かれ、Isolateが作り直されれば状態は消えます。公式ドキュメントは、Isolateが退去させられる理由としてマシンの資源不足、隔離を破ろうとする疑わしいスクリプト、個別のリソース上限の3つを挙げています。トップレベルに置くのは消えてもよいキャッシュだけにして、消えては困るデータはKV・D1・Durable Objectsへ置きます。
時計の結果には注意が要ります。セキュリティモデルの文書によれば本番のWorkersでは Date.now() は最後のI/Oの時刻を返し、実行中は進みません。ところがローカルのworkerdでは、CPUループの間に331ミリ秒進みました。処理時間を Date.now() の差で測るコードは、ローカルで動いて見えても、本番ではI/Oを挟まない区間の差が0になります。
メモリとCPUの上限:128MBはIsolate単位
| 項目 | Free | Paid |
|---|---|---|
| HTTPリクエストあたりのCPU時間 | 10ms | 既定30秒・最大5分 |
| メモリ(Isolateあたり) | 128MB | 128MB |
| Workerの起動時間 | 1秒 | 1秒 |
上限の公式ページによれば、128MBは呼び出し単位ではなくIsolate単位で、JavaScriptのヒープとWebAssemblyの割り当てを合算します。1つのIsolateが複数のリクエストを同時に処理するため、リクエストごとに大きなバッファを抱えれば、同時実行数に比例して上限に近づきます。超えると処理中のリクエストを終えてから新しいIsolateへ切り替わります。CPU時間には fetch() やKVの読み取りを待つ時間が入りません。起動時間の1秒はグローバルスコープの実行を含み、超えるとデプロイ時に Script startup exceeded CPU time limit(エラーコード10021)で拒否されます。
コールドスタートの実体:ClientHello先読みとWorker sharding
Isolateの生成が数ミリ秒でも、生成そのものが無くなるわけではありません。Cloudflareは2020年の発表で、TLSの最初のパケットであるClientHelloを受けた時点で該当ホストのWorkerを読み込み始め、ハンドシェイクが終わる頃には準備を済ませる方式を説明しました。これが「0ms cold starts」の中身で、起動を消したのではなく前倒ししたものです。
2025年の発表ではWorker shardingが加わりました。データセンター内のサーバーのうち同じWorkerを温めている1台へ、一貫性ハッシュでリクエストを寄せる仕組みです。Enterpriseの通信のうちシャーディングされたのは約4%でしたが、Worker全体の退去率は10分の1に下がったと報告されています。アクセスが少ないWorkerほど別サーバーでの再生成が減る設計です。
Isolateを実行単位にするサービスの変化:Vercel・Deno
Cloudflare以外の状況は2026年に大きく動きました。VercelのEdge Runtimeの文書はNode.jsランタイムへの移行を勧め、Next.js 16.3以降は runtime = 'edge' の指定をサポートしないと書いています。単独製品のEdge Functionsも非推奨です。Deno Deploy Classicは2026年7月20日での終了が告知され、後継はRuntimeの文書で標準DenoをLinux microVM上で動かすと説明しています。その後継も2026年10月の発表で6か月後の終了が告げられました(Deno Deploy終了と移行手順)。Isolate前提で書いたコードは、移行先のランタイムを先に確かめてください。共通APIの標準化はWinterTC(Ecma TC55)とECMA-429で扱っています。
Isolateだけでは足りない分離境界:Cloudflareの防御層とV8 Sandbox
Isolateの境界は、V8が正しく動くことを前提にしています。V8にメモリ破壊のバグが1つあれば、同じプロセス内の他のIsolateのメモリに届く可能性があります。加えてV8チームの論文(arXiv:1902.05178)はSpectreを言語の仕組みだけでは防げないと結論しています。Cloudflareのセキュリティモデルの文書は、この前提で次の層を重ねていると説明します。
| 防御層 | 内容 | 想定する脅威 |
|---|---|---|
| cordon | 信頼度別にランタイムを分ける | V8のゼロデイ |
| プロセスのサンドボックス | 名前空間とseccomp | 隔離の突破後の横展開 |
| パッチ適用 | V8修正から24時間未満で反映 | 既知のV8脆弱性 |
| 時刻とスレッドの制限 | Date.now() を実行中は固定 |
Spectre系の計時攻撃 |
| 別プロセスへの退避 | デバッガ接続時などに適用 | 検査が薄いAPIのバグ |
cordonの例として公式ドキュメントが挙げるのは、無料プランの利用者がEnterprise契約の顧客と同じプロセスに載らないことです。プロセス全体には、空のファイルシステムとseccompでファイル操作のシステムコールをすべて禁止する第2層のサンドボックスがかかり、外部との通信はUNIXドメインソケット経由で別プロセスが仲介します。
V8 SandboxとNode.js公式ビルドの差
V8自身の対策がV8 Sandboxです。V8のメモリ破壊が同じプロセスの他の領域へ波及するのを防ぐ仕組みで、2024年4月に実験段階を終えました。有効化はビルド時の v8_enable_sandbox で決まり、実行時には切り替えられません。Cloudflareは2025年9月のブログで、V8 Sandboxに加えて、CPUのメモリ保護キーをIsolateごとに割り当てる変更をV8へ入れたと説明しています。約12種類のキーをランダムに配るため、別のIsolateのヒープを読もうとする攻撃は約92%の確率でハードウェアの例外で止まります。V8 Sandbox内のメモリ破壊に限る脅威モデルでは、キーを規則的に循環させ、同じキーのサンドボックスが32GiB以内に並ばないようにしています。
nodejs.orgが配布する v26.5.0 と v26.13.0(macOS x64)の process.config.variables を見ると、v8_enable_sandbox も v8_enable_pointer_compression も0でした。今回確認したNode.js公式配布のmacOS x64版では、isolated-vmを使ってもV8 Sandboxの層は加わりません。有効・無効はNode.jsのビルド設定で確認してください。
isolated-vm単体で動かしてよい範囲の判断
isolated-vmを1つ入れただけのNode.jsサーバーで、不特定多数が書いたコードを動かすべきではありません。isolated-vmが提供するのはV8のヒープ分離だけで、表のどの層も含まないからです。社内の限られた利用者が書くスクリプトや、人がレビューしてから実行するコードなら許容できます。外部に公開するサービスでは、別プロセス化と、そのプロセスへのseccompやコンテナによる制限を組み合わせてください。workerdのREADMEも workerd is not a hardened sandbox と明記し、悪意のあるコードは仮想マシンなどの内側で動かすよう求めています。
V8の更新を即日で取り込めない体制なら、マイクロVMを選びます。Isolateの外側に置く層の設計はインフラの仕事で、自社サービスとして実行基盤を組む場合はAWS・Google Cloud・Azureのインフラ構築でも支援しています。
AI生成コードの実行基盤:Cloudflare Dynamic Workersの仕組みと料金
CloudflareはLLMが書いたコードの実行先としてIsolateを打ち出しています。2026年3月24日にDynamic Workersをオープンベータとして公開し、Workers Paidの利用者が使えるようにしました。Workerの中から、実行時に受け取ったコードで別のWorkerを起動できる機能です。呼び出し側はWranglerの設定で worker_loaders に LOADER バインディングを宣言し、env.LOADER.load() にコードを渡します。ローカルで再現するには、Miniflareをインポートし、次のコードを文字列の script として new Miniflare({ modules: true, script, compatibilityDate: '2026-08-01', workerLoaders: { LOADER: {} } }) に渡します。作成したインスタンスの dispatchFetch() で呼び出し、最後に dispose() で終了します。
export default {
async fetch(request, env) {
const worker = env.LOADER.load({
compatibilityDate: '2026-08-01',
mainModule: 'src/index.js',
modules: {
'src/index.js': `
export default {
async fetch() {
try {
await fetch('https://example.com/');
return new Response('reached');
} catch (e) {
return new Response('blocked: ' + e.message);
}
},
};
`,
},
globalOutbound: null,
});
return worker.getEntrypoint().fetch(request);
},
};
応答は blocked: This worker is not permitted to access the internet via global functions like fetch(). で始まるメッセージでした。globalOutbound を省略すると親Workerのネットワーク権限を継承するので、LLMのコードを動かすなら null を明示し、渡すバインディングも必要なものだけに絞ります。
料金ページによれば、生成数の料金は月1,000個まで込みで、超過分は1個1日あたり0.002ドルです。これとは別にWorkersプランのリクエスト数・CPU時間の料金も適用され、Dynamic Workersでは初期化時のCPU時間も課金対象になります。IDとコードの組が同じなら1個と数えますが、load() はキャッシュせず呼び出しのたびに新しいWorkerを作るので、同じコードを繰り返すなら get(id, callback) を使います。隔離の中身は前章の防御層を持つWorkersのランタイムで、seccompやcordonを自分で組む必要はありません。ネイティブの実行環境が要るコードの受け皿はCloudflare Containers、Dynamic Workerでアプリを隔離する実例はCloudflare OSとは:社内AIエージェント基盤の仕組みで取り上げています。
よくある質問
V8 Isolateの読み方は?
「ブイエイト アイソレート」と読みます。isolateは英語で「隔離する」「隔離されたもの」を意味し、V8の中で状態を隔離した実行単位を指します。Cloudflareのドキュメントや英語の資料では複数形の V8 isolates もよく使われ、検索でも両方の表記が見られます。日本語の資料では「アイソレート」とカタカナで書くか、英語のまま書くのが一般的です。
V8の「VM」は仮想マシンのことですか?
V8の埋め込みガイドは An isolate is a VM instance with its own heap. と書いていますが、ここでのVMはJavaScriptを実行する言語の仮想マシンです。OSを起動するKVMやVMwareの仮想マシンとは別物で、ハイパーバイザーの境界もありません。Isolate同士は同じOSプロセスの中で動き、分離はV8のソフトウェアが担います。
IsolateはDartやFlutterのIsolateと同じものですか?
名前は同じですが別物です。Dartの公式ドキュメントによれば、DartのIsolateは独自のメモリと単一スレッドのイベントループを持つ並行処理の単位で、メッセージ受け渡しで連携します。「状態を共有しない」という発想は似ていますが、V8のIsolateはV8エンジンのインスタンスを指す言葉で、Dartのランタイムとは実装が別です。
Node.jsはいくつのIsolateを使っていますか?
1つのNode.jsプロセスのメインスレッドは1つのIsolateで動きます。Worker Threadsで Worker を作るたびに、別スレッドで新しいJSエンジンのインスタンスが加わります。cluster や child_process は別プロセスを起動するので、プロセスごとに別のV8が動きます。vm.createContext() はContextを増やすだけで、Isolateの数は変わりません。
isolated-vmは本番環境で使えますか?
社内の限られた用途なら使えます。ただしREADMEはプロジェクトをmaintenance modeとしており、V8の更新に追従し続ける保証はありません。対応表にあるのはNode.js 22・24・26の偶数版で、Node.js 26なら7.x系を使います。不特定多数のコードを動かす用途では、isolated-vmだけに頼らず、別プロセス化とOS側の制限を組み合わせるか、マネージドの実行基盤を選びます。