はじめに
ARM向けChromiumのクロスビルドとUPXによるバイナリフットプリントの最適化 では、UPX で Chromium の実行ファイルを圧縮し、ストレージ (ROM) の使用量を削減しました。しかし「UPX で圧縮するとプロセス間でメモリを共有できなくなり、RAM の使用量が増える」という点について未調査でした。
ROM を削減した結果として RAM が増えるのであれば、RAM に余裕のない機器では UPX の利用が難しいでしょう。そこで、次の 3 点を計測しました。
- RAM 使用量はどれだけ増加するか
- 増加の原因は何か
- 増加を抑制する方法はあるか
計測は Apple Silicon の Mac 上の Docker で行い、対象は次の 2 つです。前回の記事で使用した自前ビルドの Chromium とは別のバイナリです。
| 対象 | 実行方式 | 実行ファイルのサイズ (圧縮前 → UPX) |
|---|---|---|
| Google Chrome 157 (x86_64) | Rosetta によるエミュレーション | 293 MiB → 101 MiB |
| Chromium 156 (arm64、Debian パッケージ) | ネイティブ | 294 MiB → 99 MiB |
x86_64 の Google Chrome は --single-process で起動できなかったため、単一プロセス構成の計測は arm64 の Chromium だけで行いました。
検証結果の概要
arm64 の Chromium 156、1 タブでの結果です。ROM は 3 つの起動方法とも約 190 MiB 減ります。
| 起動方法 | メモリに余裕があるときの RAM の増加 | メモリが不足したときの RAM の増加 (最大) |
|---|---|---|
| 通常 | +922 MiB | +1124 MiB |
マルチプロセスのまま execve() を 2 回に削減 |
+362 MiB | +564 MiB |
--single-process --no-zygote |
+82 MiB | +283 MiB |
RAM の増加は、メモリに余裕があるかどうかで変わります。圧縮前と UPX 版で、コードの置かれ方が次のように異なるためです。
- 圧縮前のコードは、ディスク上のファイルの写しとして RAM に置かれます。メモリが不足すると、カーネルはこの写しを RAM から解放できます。必要になった時点でディスクから読み直します。
- UPX 版のコードは、起動時に RAM 上へ展開されます。ディスク上に同じ内容がないため、メモリが不足してもカーネルは解放できません。
--single-process --no-zygote の場合、2 つの列は次の状態に対応します。図の数値の単位は MiB です。
- メモリに余裕があるとき (+82 MiB): 圧縮前も UPX 版も、コードを RAM に置いています。UPX 版は実行されないコードも展開するため、その分だけ多く使います。
- メモリが不足したとき (+283 MiB): 圧縮前は、ファイルの写しを最大で 240 MiB 解放できます。UPX 版で解放できるのは 39 MiB です。この差の約 200 MiB が、82 MiB に上乗せされます。
カーネルによるこの解放は、OOM Killer がプロセスを終了させるより前の段階で行われます。UPX 版は解放できる RAM が少ないため、圧縮前よりも早く OOM Killer が動作する状態に達すると考えられます。メモリを不足させた状態での挙動は計測していません。右の列は、圧縮前のコードがすべて解放された場合の値です。
本文では、右の列を「回収できない RAM の増加」として主に扱います。x86_64 の Google Chrome 157 でも、通常起動でのこの値は +1110 MiB でした。
- 増加の原因は、Chromium が自身を
execve()するたびに UPX の展開処理が実行され、展開後のコードがプロセス間で共有されないことです。execve()1 回あたり約 280 MiB 増加します。 -
--single-process --no-zygoteでプロセスを 1 つにすると、execve()1 回分の 283 MiB まで下がります。この 283 MiB は UPX がバイナリ全体を RAM 上に展開することに由来し、本記事で試した起動オプションと UPX のオプションでは削減できませんでした。 - マルチプロセスのままでも、起動オプションで
execve()を 2 回に減らすと +564 MiB まで下がります。残る 1 回をfork()に置き換えるパッチは検討のみで、作成していません。
RAM 使用量の定義
Linux は RAM をページ単位で管理しています。ページは、同じ内容がディスク上にあるかどうかで 3 種類に分かれます。
| 種類 | 内容 | 例 |
|---|---|---|
file (file-backed ページ) |
ディスク上のファイルを読み込んだ写し | 実行ファイルのコード |
anon (匿名ページ) |
プロセスが実行中に作成したデータ。ディスク上に元がない | ヒープ、スタック |
shmem |
RAM 上にだけ存在するファイル。ディスク上に元がない | tmpfs、memfd |
memfd は memfd_create() で作成する、RAM 上にだけ存在する無名のファイルです。後述するとおり、UPX で圧縮したバイナリはコードを memfd にマップします。
この 3 種類は、メモリが不足したときの扱いが異なります。
-
fileページは、カーネルが破棄して空きを作れます。内容はディスクにあるので、再度アクセスされた時点でファイルから読み直します。 -
anonとshmemのページは、破棄すると内容が失われます。スワップがあればそこへ退避できますが、スワップがなければ RAM に残り続けます。
本記事の「RAM」は、回収できない anon と shmem の合計です。コンテナ全体の値を cgroup v2 の memory.stat から取得しました。
grep -E '^(anon|shmem) ' /sys/fs/cgroup/memory.stat
実行ファイルのコードは file-backed ページなので、この定義では圧縮前のバイナリのコードは RAM に計上されません。つまり本記事の「RAM の増加」は、回収できないメモリの増加量です。メモリに余裕がある間の実際の差はこれより小さくなります。file-backed ページを含めた比較は「file-backed ページを含めた比較」に記載します。
RAM 増加量の計測
x86_64 の Google Chrome 157 で、1 つのブラウザのタブ数を変えて RAM を計測しました。タブはすべて別サイトです。
| タブ数 | プロセス数 | 圧縮前 | UPX | 差 |
|---|---|---|---|---|
| 1 | 9 | 440 MiB | 1550 MiB | +1110 MiB |
| 5 | 13 | 748 MiB | 1851 MiB | +1103 MiB |
| 10 | 18 | 1184 MiB | 2253 MiB | +1069 MiB |
差は約 1.1 GiB で、タブ数に依存しません。UPX による増加はタブごとではなく、ブラウザの起動時に一度だけ発生します。
arm64 の Chromium 156 でも、1 タブで圧縮前 162 MiB、UPX 1286 MiB となり、差は +1124 MiB でした。
RAM が増加する原因
圧縮前と UPX 圧縮後で、コードが RAM 上のどこに置かれるかを次の図に示します。図のプロセスは、Chromium が execve() で起動する 4 つです。以降の項で、計測結果からこの図の内容を確認します。
圧縮前のコード共有
圧縮前の Chrome では、全プロセスが同じ chrome ファイルをマップします。カーネルは同一ファイルの同一オフセットを 1 つの物理ページ (ページキャッシュ) で管理するため、プロセス数にかかわらずコードの実体は 1 つです。
x86_64 の 1 タブ (9 プロセス) の状態で chrome ファイルのマッピングを集計すると、RSS の合計は 767 MiB、PSS の合計は 239 MiB でした。RSS は共有ページを各プロセスで重複して数え、PSS は共有ページを共有しているプロセス数で按分します。9 プロセスが 239 MiB の物理ページを共有していることになります。
UPX 圧縮後の execve() ごとの展開
UPX 版の Chrome では、コードは chrome ファイルではなく、/memfd:upx という名前の memfd にマップされています。/proc/<pid>/maps では次のように表示されます。
7fffea76b000-7ffff8aff000 r-xs 00000000 00:01 6256 /memfd:upx (deleted)
UPX の展開処理が、起動時に memfd を作成してコードを展開していると考えられます。memfd は展開処理を実行したプロセスが作成するので、中身が同じでも、別々に展開されたものは別の物理ページになります。
x86_64 の 1 タブの状態で、各プロセスがマップしている memfd の inode を調べました。TYPE は /proc/<pid>/cmdline の --type の値です。
PID PPID TYPE inode
5995 5989 browser 2687
6005 5995 zygote 741
6006 5995 zygote 7822
6036 6005 zygote 741
6037 5995 utility 7825
6039 6006 zygote 7822
6041 6006 zygote 7822
6065 6006 zygote 7822
6066 6006 zygote 7822
inode は 4 種類あり、展開済みコードのコピーが 4 つ存在します。
- ブラウザ本体 (5995)、2 つの zygote (6005、6006)、utility (6037) は、それぞれ別の inode です。zygote と utility は、ブラウザ本体が自身を
execve()して起動したプロセスです (PPID が 5995)。execve()はプロセスのメモリマッピングを破棄するため、UPX の展開処理が再度実行され、新しい memfd が作られます。 - PPID が 6005 または 6006 の 5 プロセスは、zygote から
fork()で生成された子プロセス (レンダラなど) です。fork()は親のマッピングを継承するため、親の zygote と同じ inode になります。cmdlineも親から引き継ぐため、TYPE は zygote と表示されています。
タブ数を増やしても差が広がらなかったのは、レンダラが fork() で生成されるためです。
増加量の内訳
x86_64 の 1 タブでの増加 +1110 MiB を、cgroup の項目別に見ると次のとおりです。
| 項目 | 圧縮前 | UPX | 差 |
|---|---|---|---|
shmem |
7 MiB | 917 MiB | +910 MiB |
anon |
433 MiB | 633 MiB | +200 MiB |
shmem の増加は memfd に展開されたコードです。4 つで 910 MiB なので、1 つあたり約 228 MiB です。
anon の増加は、コード以外の領域の展開先と考えられます。ブラウザ本体のプロセスでは、51.8 MiB の読み取り専用領域が匿名ページとして存在していました。4 プロセス分で約 200 MiB となり増加量と合いますが、他の 3 プロセスは確認していません。
execve() の回数と増加量の関係
arm64 の Chromium 156 では、起動オプションによって execve() されるプロセスの数が変わります。UPX の展開が実行されるプロセス数と、RAM の増加量の関係は次のとおりです。
| 起動オプション | 展開が実行されるプロセス数 | RAM の増加 (1 タブ) | 1 プロセスあたり |
|---|---|---|---|
| なし | 4 | +1124 MiB | 281 MiB |
--single-process |
3 | +847 MiB | 282 MiB |
--single-process --no-zygote |
1 | +283 MiB | 283 MiB |
増加量は、展開が実行されるプロセス数に比例しています。
RAM 増加の抑制方法
増加量は UPX の展開が実行される回数に比例するので、抑制するには execve() されるプロセスを減らします。
単一プロセス構成
arm64 の Chromium 156 で、起動オプションを変えて計測しました。
| 起動オプション | プロセス数 | タブ数 | 圧縮前 | UPX | 差 |
|---|---|---|---|---|---|
| なし | 9 | 1 | 162 MiB | 1286 MiB | +1124 MiB |
| なし | 13 | 5 | 244 MiB | 1373 MiB | +1129 MiB |
--single-process |
3 | 1 | 121 MiB | 968 MiB | +847 MiB |
--single-process |
3 | 5 | 170 MiB | 1016 MiB | +846 MiB |
--single-process --no-zygote |
1 | 1 | 92 MiB | 375 MiB | +283 MiB |
--single-process --no-zygote |
1 | 5 | 141 MiB | 423 MiB | +282 MiB |
--single-process だけではプロセスは 1 つになりません。zygote が 2 つ起動されたまま残り、それぞれが展開コピーを持ちます。--no-zygote を併用するとプロセスは 1 つになり、増加は 283 MiB になります。
ただし、--single-process は Chromium がサポート対象としていないオプションです。レンダラがブラウザ本体と同じプロセスで動作するため、サンドボックスによる分離がなく、レンダラの異常終了がブラウザ全体の終了につながります。
x86_64 の Google Chrome 157 は、圧縮前のバイナリでも --single-process を指定すると Segmentation fault で起動しませんでした。Rosetta の影響か、Google Chrome のビルドの違いかは切り分けていません。
ブラウザの複数起動の回避
x86_64 の Google Chrome 157 で、別の --user-data-dir を指定してブラウザを 2 つ起動すると (各 3 タブ)、圧縮前 1234 MiB に対して UPX は 3445 MiB で、差は 2211 MiB でした。ブラウザ 1 つ分の増加が 2 回発生しています。
圧縮前のバイナリでは、別々に起動したブラウザの間でも chrome ファイルのページキャッシュが共有されます。UPX 版ではこの共有が失われます。
単一プロセス構成で残る増加分の内訳
arm64 の単一プロセス構成で、バイナリに由来するマッピングを /proc/<pid>/smaps で比較しました。数値の単位は MiB です。
圧縮前
Size Rss Anon perm path
44.3 39.2 0.0 r--p /usr/lib/chromium/chromium
237.3 160.3 0.0 r-xp /usr/lib/chromium/chromium
11.3 11.3 11.3 r--p /usr/lib/chromium/chromium
UPX 圧縮後
Size Rss Anon perm path
44.3 44.3 44.3 r--p [anon]
237.3 160.3 0.0 r-xs /memfd:upx
11.3 11.3 11.3 r--p [anon]
3 行目の領域は圧縮前後とも匿名ページで、差に寄与しません。差が生じるのは 1 行目と 2 行目です。
圧縮前は、コード (r-x、237.3 MiB) と読み取り専用データ (r--、44.3 MiB) がどちらも chromium ファイルのマッピングです。デマンドページングにより、RAM に読み込まれるのはアクセスされたページだけで、コードは 160.3 MiB、読み取り専用データは 39.2 MiB でした。
UPX 圧縮後は、コードが memfd に、読み取り専用データが匿名ページに、それぞれ全体が展開されます。cgroup の値は次のように変化しました。
| 項目 | 圧縮前 | UPX | 差 | 対応する領域 |
|---|---|---|---|---|
shmem |
4 MiB | 242 MiB | +238 MiB | コード |
anon |
89 MiB | 134 MiB | +45 MiB | 読み取り専用データ |
UPX 版でプロセスがアクセスしたコードは圧縮前と同じ 160.3 MiB ですが、shmem は 238 MiB 増えています。アクセスされていないページも、展開済みの状態で RAM を占有しています。
増加分の 283 MiB は、次の 2 つに分けられます。
- 約 200 MiB (160.3 MiB + 39.2 MiB): 圧縮前でも RAM 上にある内容です。圧縮前はカーネルが破棄できる file-backed ページでしたが、UPX 版では破棄できないページになります。
- 約 80 MiB: 圧縮前は RAM に読み込まれなかった部分です。UPX は起動時にバイナリ全体を展開するため、実行されないコードも RAM に置かれます。
file-backed ページを含めた比較
ここまでの RAM は file-backed ページを含んでいません。全プロセスの Pss_File (/proc/<pid>/smaps_rollup) の合計を RAM に加えると、arm64 の 1 タブでは次のようになります。
| 起動オプション | 圧縮前 | UPX | 差 |
|---|---|---|---|
| なし | 404 MiB | 1326 MiB | +922 MiB |
--single-process --no-zygote |
332 MiB | 414 MiB | +82 MiB |
メモリに余裕があり、カーネルが file-backed ページを破棄していない状態では、UPX による増加はこの値になります。単一プロセス構成では +82 MiB です。
メモリが不足すると、圧縮前のバイナリではカーネルが file-backed ページを破棄して空きを作れますが、UPX 版ではその分が回収できません。このときの差が、前節までの +1124 MiB や +283 MiB です。メモリを不足させた状態での挙動は計測していません。
UPX 採用の判断
通常の起動方法では、ROM の削減量 (約 190 MiB) に対して、回収できない RAM が約 1.1 GiB 増えます。file-backed ページを含めて比較しても +922 MiB です。RAM に制約のある機器では、ROM の削減量に見合いません。
--single-process --no-zygote では、回収できない RAM の増加が 283 MiB、file-backed ページを含めた増加が 82 MiB まで下がります。ROM を約 190 MiB 削減するために 283 MiB の回収できない RAM 増加を許容でき、かつサンドボックスとプロセス分離を失うことを許容できる場合に限り、採用の余地があります。
ROM の削減だけが目的であれば、Chromium を squashfs や erofs などの圧縮ファイルシステムに配置する方法があります。この方法ではファイルのマッピングが維持されるため、プロセス間の共有とページの破棄が圧縮前と同様に機能すると考えられます。ROM の削減量と RAM の使用量は計測していません。
execve() を fork() に置き換えるパッチの検討
単一プロセス構成はプロセス分離を失います。マルチプロセスのまま展開を 1 回に抑える方法として、Chromium が自身を execve() している箇所を fork() に置き換えるパッチを検討しました。fork() で生成したプロセスは親の memfd を継承するため、展開は実行されません。レンダラがすでにこの方式で生成されています。
パッチは作成していません。この節の内容は、起動オプションによる計測、Chromium 156 のソースの確認、およびそこからの推定です。
起動オプションによる execve() の削減
パッチを作成する前に、起動オプションだけで execve() の回数をどこまで減らせるかを arm64 の 1 タブで計測しました。
| execve() の回数 | 追加した起動オプション | プロセス数 | 圧縮前 | UPX | 差 |
|---|---|---|---|---|---|
| 4 | なし | 9 | 158 MiB | 1286 MiB | +1128 MiB |
| 3 | --enable-features=NetworkServiceInProcess2 |
8 | 143 MiB | 989 MiB | +846 MiB |
| 2 | 上記に加えて --no-unsandboxed-zygote --in-process-gpu
|
6 | 126 MiB | 690 MiB | +564 MiB |
| 1 | --single-process --no-zygote |
1 | 92 MiB | 375 MiB | +283 MiB |
execve() の回数は、ブラウザ本体の起動を含みます。1 回あたりの増加は 282 MiB で一定です。4 回の行は再計測した値で、「RAM 増加量の計測」の値とは数 MiB の差があります。
各オプションの役割は次のとおりです。
-
--enable-features=NetworkServiceInProcess2: ネットワークサービスをブラウザ本体のプロセス内で実行します。utility プロセスが起動されなくなります。 -
--no-unsandboxed-zygote: 2 つの zygote のうち、サンドボックスを適用しないプロセス用の zygote を起動しません。 -
--in-process-gpu: GPU の処理をブラウザ本体のプロセス内で実行します。--no-unsandboxed-zygoteだけを指定すると、GPU プロセスが zygote からのfork()ではなくexecve()で起動されるようになり、shmemは 955 MiB のまま減りませんでした。
execve() が 2 回の構成で、各プロセスがマップしている memfd の inode は次のとおりです。
PID PPID TYPE inode
332 7 browser 25201
340 332 zygote 32033
389 340 zygote 32033
390 340 zygote 32033
394 340 zygote 32033
400 340 zygote 32033
inode は 2 種類で、残っている execve() はブラウザ本体による zygote の起動だけです。レンダラは別プロセスのままです。
パッチの対象箇所
Chromium 156.0.8078.79 のソースで、次の点を確認しました。
- zygote は
content/browser/zygote_host/zygote_host_impl_linux.ccのZygoteHostImpl::LaunchZygote()で起動されます。この関数がbase::LaunchProcess()を呼び、execve()が実行されます。 -
LaunchZygote()はcontent/app/content_main_runner_impl.ccのInitializeZygoteSandboxForBrowserProcess()から呼ばれます。この関数には、呼び出しより前にスレッドを作成してはならないというコメントがあります。zygote を起動する時点で、ブラウザ本体はシングルスレッドです。 - ネットワークサービスは、
content/browser/service_host/utility_sandbox_delegate.ccのUtilitySandboxedProcessLauncherDelegate::GetZygote()で、zygote を使わずに起動する分岐になっています。zygote からfork()する方式への変更は、同じ関数内のコメントに TODO (crbug.com/40261714) として記載されています。コメントによると、現状でfork()にするとブラウザテストが失敗することが、この分岐を残している理由です。
パッチが成立した場合の RAM 増加量の推定
LaunchZygote() の execve() を fork() に置き換えられた場合、zygote とレンダラはブラウザ本体の memfd を継承し、展開は 1 回になります。表の関係から、RAM の増加は +283 MiB 前後になると推定します。
単一プロセス構成の内訳で示した、匿名ページに展開される読み取り専用データ (44.3 MiB) は、fork() の後も書き込まれないかぎり親子で共有されます。この分がプロセス数に比例して増えることはないと考えられます。
パッチの課題
次の点は確認していません。
-
fork()で生成した子プロセスは、ブラウザ本体として進めた初期化の状態を引き継ぎます。子プロセス側でコマンドライン (--type=zygote) などを zygote 用に設定し直す必要があります。必要な変更の範囲は不明です。 - サンドボックスを有効にした場合、zygote は名前空間サンドボックスまたは setuid サンドボックスを経由して起動されます。setuid サンドボックスは別の実行ファイルを
execve()する方式のため、fork()には置き換えられません。 -
fork()で生成すると、ブラウザ本体とレンダラのアドレス配置が同一になります。レンダラが侵害された場合に、ブラウザ本体への攻撃が容易になります。 - 計測は
--headless=new --no-sandbox --disable-gpuで行いました。GPU を使用する構成で--in-process-gpuが動作するかは確認していません。
起動オプションだけで +564 MiB まで下がるため、パッチで追加で削減できる量は約 280 MiB です。
未検証の項目
- 製品に近い構成での RAM 使用量。計測は
--headless=new --no-sandbox --disable-gpuで行いました。サンドボックスと GPU プロセスを有効にした場合、execve()されるプロセスの数が変わるかは確認していません - メモリ不足時の挙動、およびスワップを有効にした場合の挙動
- 圧縮ファイルシステムを使用した場合の ROM と RAM
再現手順
| 項目 | x86_64 | arm64 |
|---|---|---|
| ベースイメージ | ubuntu:24.04 | debian:trixie |
| ブラウザ | Google Chrome 157.0.8103.50 | Chromium 156.0.8078.79 |
| バイナリのパス | /opt/google/chrome/chrome |
/usr/lib/chromium/chromium |
| UPX | 5.0.2 | 5.0.2 |
| 圧縮結果 | 306789704 → 106202348 バイト | 308415392 → 103286284 バイト |
UPX のバージョン
apt でインストールされる UPX では計測できなかったため、GitHub のリリース の UPX 5.0.2 を使用しました。
- x86_64 の Ubuntu 24.04 の UPX 4.2.2 で圧縮したバイナリは、Rosetta 上では
lsも Chrome も Segmentation fault で起動しませんでした。 - arm64 の Debian trixie の UPX 4.2.4 で圧縮した Chromium は、通常の起動では動作しましたが、
--single-processを指定すると SIGTRAP で終了しました。原因は調べていません。
Dockerfile
x86_64
FROM --platform=linux/amd64 ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends wget ca-certificates procps xz-utils \
&& wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
&& apt-get install -y --no-install-recommends ./google-chrome-stable_current_amd64.deb \
&& rm google-chrome-stable_current_amd64.deb && rm -rf /var/lib/apt/lists/*
RUN wget -qO- https://github.com/upx/upx/releases/download/v5.0.2/upx-5.0.2-amd64_linux.tar.xz \
| tar xJ --strip-components=1 -C /usr/local/bin upx-5.0.2-amd64_linux/upx
arm64。Ubuntu の chromium パッケージは snap でコンテナ内では動作しないため、Debian を使用しています。
FROM debian:trixie
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends chromium wget ca-certificates procps xz-utils \
&& rm -rf /var/lib/apt/lists/*
RUN wget -qO- https://github.com/upx/upx/releases/download/v5.0.2/upx-5.0.2-arm64_linux.tar.xz \
| tar xJ --strip-components=1 -C /usr/local/bin upx-5.0.2-arm64_linux/upx
コンテナの起動
--init は終了した子プロセスを回収するため、--shm-size は /dev/shm の容量を確保するために指定しています。arm64 の例です。
docker build --platform linux/arm64 -f Dockerfile.arm64 -t upx-chromium-arm64 .
docker run -d --init --shm-size=2g --name upxarm upx-chromium-arm64 sleep infinity
docker exec -it upxarm bash
圧縮
以降はコンテナ内で実行します。
cd /usr/lib/chromium
cp -p chromium /chromium.orig
upx chromium
ブラウザの起動とタブの追加
headless モードは起動時に複数の URL を指定できないため、2 つ目以降のタブは DevTools の HTTP API で追加します。
./chromium --headless=new --no-sandbox --disable-gpu \
--user-data-dir=/tmp/prof1 --remote-debugging-port=9222 https://example.com >/dev/null 2>&1 &
sleep 10
wget -q --method=PUT -O /dev/null "http://127.0.0.1:9222/json/new?https://www.wikipedia.org"
単一プロセス構成では、起動オプションに --single-process --no-zygote を追加します。ブラウザを 2 つ起動する場合は、--user-data-dir と --remote-debugging-port を別の値にします。
RAM は、タブを追加してから x86_64 では 30 秒後、arm64 では 15 秒後に読み取りました。
計測
RAM 使用量、値はバイト単位です。
grep -E '^(anon|shmem) ' /sys/fs/cgroup/memory.stat
各プロセスがマップしている memfd。5 列目が inode で、同じ inode のプロセスは展開済みコードを共有しています。
grep -H 'memfd:upx' /proc/[0-9]*/maps
バイナリに由来するマッピングの RSS と PSS の合計。x86_64 では chromium$ を chrome$ に置き換えます。
cat /proc/[0-9]*/smaps 2>/dev/null | awk '
/^[0-9a-f]+-[0-9a-f]+ / { t = ($6 ~ /chromium$|memfd:upx/) }
t && $1=="Rss:" { r += $2 }
t && $1=="Pss:" { p += $2 }
END { printf "RSS %.0f MiB, PSS %.0f MiB\n", r/1024, p/1024 }'



