ARM64_Lab

Java 8 x64 の java -version は160.3msかかった

この記事の見出し
  1. 起動してすぐ終わるコマンドだけを見る
  2. CLI の中でどの位置にいるか
  3. 64-Bit という表示で安心しかけた
  4. 呼び出し方を変える

java -version の中央値は160.3msで、同じ日に測った curl --version の16.3msより9.8倍遅かった。

引っかかったのは、遅さそのものよりも表示の紛らわしさだった。java -version には OpenJDK 64-Bit Server VM と出るので、ぱっと見ると64bitで動いていることは分かる。けれど ARM64 Windows では、その64bitがARM64ネイティブを意味するとは限らず、実際には別の話として確認する必要がある。PEヘッダーを読むと Machine は 0x8664、つまりx64だった(ここでようやく表示だけでは足りないと分かった)。

使っていたのは Eclipse Adoptium の jdk-8.0.492.9-hotspot。パスは C:\Program Files\Eclipse Adoptium\jdk-8.0.492.9-hotspot\bin\java.exe で、Surface Pro 11th Edition の ARM64 Windows 上ではエミュレーション側で起動している。2026年6月27日に測り、記録ファイル上の日付も2026-06-27として扱った。SoC は Snapdragon X 12-core X1E80100 @ 3.40GHz、メモリは32GB、OS は Windows 11 Pro 10.0.26200 の ARM64 版。電源はAC接続で、電源プランは既定のバランスのままにしている。

起動してすぐ終わるコマンドだけを見る

測ったのはアプリケーションの処理性能ではなく、プロセスを起動してすぐ終了するまでの時間だけ(短いツールを何度も呼ぶ場面を先に疑っていた)。Java は java -version、curl は curl --version、Python は python -c pass のように、各ツールにとって短いコマンドを5回ずつ実行した。

1..5 | ForEach-Object {
    (Measure-Command { java -version }).TotalMilliseconds
}

PEヘッダーも同じ流れで読んだ。見ているのは Machine フィールドで、ARM64なら 0xaa64、x64なら 0x8664 になる。java -version の文字列を信用しすぎず、実行ファイルそのものを見に行った(ここを飛ばしていたら、たぶん勘違いしたままだった)。

$bytes = [System.IO.File]::ReadAllBytes($path)
$peOffset = [BitConverter]::ToInt32($bytes, 0x3c)
$machine = [BitConverter]::ToUInt16($bytes, $peOffset + 4)

手元では JDK 8 の ARM64 Windows 版を見つけられないままだった(古い前提のツールを動かす都合があり、新しいJDKへ逃がす判断をこの日は取らなかった)。JDK 17 以降なら別の選択肢がありそうだが、この日は古いJava 8前提のツールを動かす目的だったので、まず現物の java.exe で確かめることにした(新しい版で測れば別の表になる可能性を、ここでは脇へ置いた)。

CLI の中でどの位置にいるか

5回分をそのまま置く。中央値だけを見ると curl が最速で16.3ms、次が Python の36.3ms、Git が70.0ms、Node.js が86.1ms。Java は160.3msなので、ARM64ネイティブの軽いCLI群とは別の場所にいる。

ツール PEアーキ 中央値 全5回
curl --version ARM64 16.3ms 87.3 / 16.3 / 18.6 / 16.3 / 15.5
python -c pass ARM64 36.3ms 43.4 / 33.9 / 36.3 / 38.2 / 35.7
git --version ARM64 70.0ms 80.6 / 57.0 / 70.0 / 82.6 / 67.0
node -e 0 ARM64 86.1ms 93.7 / 76.2 / 93.1 / 86.1 / 80.8
java -version x64 160.3ms 157.3 / 154.6 / 160.3 / 188.0 / 187.1
powershell -NoProfile -c exit ARM64 192.5ms 219.7 / 731.6 / 177.5 / 178.0 / 192.5
pwsh -NoProfile -c exit ARM64 289.6ms 289.6 / 274.9 / 337.8 / 274.6 / 365.6

curl の1回目だけ87.3msに跳ねている(この初回だけを見ると、別の話に引っ張られそうになる)。2回目以降は15.5msから18.6msに収まっているので、初回のファイルキャッシュや周辺の揺れを拾ったのかもしれない。Java も154.6msから188.0msまで振れたが、curlほどの初回だけの段差には見えなかった。

PowerShell 系は別の記事で扱ったので、ここでは脇役として置いた(比較対象として残すだけで、今回の主役にはしない)。表に入れたのは、Java の160.3msがどのあたりに位置するかを見るため。Windows PowerShell 5.1 の192.5msよりは速く、pwsh の289.6msよりも速い。とはいえ、短いCLIを何度も呼ぶ前提では、curlやPythonとの差が積み上がる。

64-Bit という表示で安心しかけた

正直、最初は OpenJDK 64-Bit Server VM という表示で少し安心しかけた。64bitならよいのでは、と思ってしまう(この時点の予想と違ったのは、64bit表示が安心材料にならなかったところ)。けれど今回の 64-Bit Server VM はx64の64bitであって、ARM64ネイティブという意味ではなかった(この表記はなかなか紛らわしい)。

ここを間違えると、測定結果の読み方もずれる。java -version が160.3msかかったという数字だけを見て、すぐに「エミュレーションだから遅い」と言いたくなるが、JVMの起動処理そのものにも重い部分があるので、表示、PEヘッダー、実測時間をばらばらに見ないと話が混ざってしまう。PEはx64なので、その疑いは自然だろう。

ただ、この測定だけで因果まで切るのは難しい(x64という事実とJVM起動の重さを一緒に畳み込みすぎる)。JVMはもともと起動時にランタイムを立ち上げ、クラスローダーやVM初期化を通る。Java 8のx64実行ファイルであることと、160.3msという遅さがどれだけ結びついているのか。そこは ARM64版JDK 17 などを入れて同じ条件で測らないと分からないはず。

今回の失敗は、結論を急ぎたくなったこと。x64である事実はPEヘッダーから言えるし、遅いことも5回の測定から言える。けれど「x64だから160.3msになった」と言える段階ではなかった。この点は分けて扱う。

呼び出し方を変える

実運用では、Javaを使うツールを起動のたびに叩く自動化から外してみた。1回だけなら160.3msは待てるし、人間が手で実行するなら気にならないことも多い(問題になるのは、短い処理を小刻みに呼び出す作りのほうだ)。困るのは短い処理を何度も呼ぶ形。

例えば小さな変換や検査をファイルごとに java プロセスとして起動すると、処理本体より起動時間のほうが目立ってしまう(実際の自動化では、この形を避けないと小さい待ち時間が何度も積み上がる)。そういう場所は、常駐できる形にするか、Pythonやcurlのような軽いARM64ネイティブCLIへ寄せた。数字を見てから直したのは、この呼び出し方のほう。

JDK 8を使う理由があるなら、手元では選択の余地がかなり狭い。ARM64 Windows版を見つけられなかったので、x64で動かす形になった。逆に、バージョンを上げられるツールなら、ARM64版JDKで測り直す価値がある。

今回分かったのは、java -version という最小の確認でも160.3msかかり、その java.exe はx64だったというところまで。次に比べるなら、同じSurface ProでARM64版JDK 17を入れ、同じ5回測定でJVM起動そのものとエミュレーション分を分けてみたい。これは次回へ持ち越す。

a
arm64lab — 個人運営

Surface Pro 11th Edition(Snapdragon X Elite)を2025年5月から常用機にしている個人の記録です。ARM64 版 Windows で詰まったところと、その場で測った値をそのまま書き残しています。特定の企業・団体とは関係がなく、いかなる組織を代表する見解でもありません。