Javaの基幹バッチやAPIサーバーをKubernetes上で長年運用しているチームに向けた内容です。Java 26でProject Panama(GPU等のネイティブライブラリをJNIより低コストで呼び出す仕組み)が安定版になり、Project Loom由来の仮想スレッド(軽量スレッドで大量の同時リクエストを処理する仕組み)も実務投入が進んでいます。ただ、既存の業務コードベースにこの2つを持ち込むと、意外な場所でつまずくケースが見えてきています。
何が起きるか
最初に起きやすいのは、JNI(JavaからC/C++の関数を呼び出す既存の仕組み)を前提に書かれた既存の連携コードが、Panama移行の対象から漏れることです。Panamaが解決するのはあくまで新規に書くネイティブ連携のオーバーヘッドで、既存JNIコードは自動的には速くなりません。
次に起きやすいのが、仮想スレッド導入によるDBコネクションプール枯渇です。RAG(検索拡張生成、外部データを検索してLLMの回答精度を上げる仕組み)のような大量同時リクエストを仮想スレッドで捌けるようになった結果、従来のプラットフォームスレッド前提で設計したコネクションプールのサイズ設定がボトルネックとして表面化します。
3つ目は、KubernetesのDRA(Dynamic Resource Allocation、GPU・NPUなどの専用ハードウェアをより細かくスケジューリングする仕組み)を使ったAI推論用Podが、既存の起動時間監視の閾値に引っかかって誤検知されることです。Kubernetes 1.35では起動レイテンシを直接メトリクスとして公開できるようになりましたが、モデルの初期化に時間がかかる推論Podは、既存Webアプリの起動時間基準では「異常」に見えてしまいます。
なぜ起きるか
根本原因を分解すると、いずれも「新しい実行モデルを、古い前提の設定にそのまま乗せている」という構図です。
Panamaはjava.lang.foreignパッケージ経由でネイティブメモリに直接アクセスできますが、既存のJNIコードとは別のAPI体系です。コードを書き換えない限り恩恵は受けられません。移行漏れが起きるのは、この「新旧が共存する」性質を認識せずに、バージョンアップだけで速くなると期待してしまうためです。
コネクションプール枯渇は、仮想スレッドの特性への誤解が原因です。仮想スレッドは数千〜数万の同時実行に耐えますが、DBコネクションのような有限リソースへのアクセスはスレッド数を増やしても解決しません。むしろ同時に大量のリクエストがコネクションプールに殺到しやすくなります。HikariCPなど従来のプールは、プラットフォームスレッド数を前提にmaximumPoolSizeを決めていることが多く、仮想スレッド前提の見直しがされていないと詰まります。
Pod起動監視の誤検知は、Kubernetesの新しいオブザーバビリティ機能を、既存のアラート設計にそのまま流用してしまうことが原因です。起動時間の分布がAI推論Podと通常のWebアプリでは根本的に異なるのに、同じ閾値・同じダッシュボードで見てしまうと、正常な起動遅延を障害と誤認します。
自分のプロジェクトが該当するか確認する
まず、実行環境のJDKバージョンを確認します。
java -versionJDK 26系であることに加えて、java.lang.foreignを使っているコードとJNIを使っているコードが混在していないか、grepでネイティブ連携箇所を洗い出します。
grep -rl "native " --include="*.java" src/ | xargs grep -l "System.loadLibrary\|JNIEnv"次に、仮想スレッドを使っているかどうかは、起動オプションやコード上のExecutors.newVirtualThreadPerTaskExecutor()の有無で確認できます。Spring Bootであればapplication.propertiesのspring.threads.virtual.enabled=trueが該当箇所です。
コネクションプールの設定は、HikariCPならspring.datasource.hikari.maximum-pool-sizeの値と、実際のDB側のmax_connections(PostgreSQLの場合)を突き合わせて確認します。仮想スレッド導入後にこの値を見直していなければ要注意です。
Kubernetes側は、バージョンと起動時間メトリクスの有無を確認します。
kubectl version --short
kubectl get --raw /metrics | grep pod_start_duration1.35未満であればDRAの起動監視機能自体が使えないため、既存のlivenessProbeのinitialDelaySeconds設定を手動で見直す必要があります。
対策の手順
Panama移行は、影響範囲を絞って段階的に進めます。まず前述のgrepで洗い出したJNI連携箇所のうち、パフォーマンス要件が厳しい箇所(推論呼び出しなど)だけをjava.lang.foreignベースに書き換え、それ以外は既存のまま残す判断が現実的です。全面書き換えは工数もリスクも大きく、業務システムでは優先度付けが欠かせません。
仮想スレッド導入時は、コネクションプールのサイズをリクエスト同時実行数ではなく、DB側が処理できる実コネクション数を基準に再設計します。目安として、HikariCPの公式ドキュメントはmaximum-pool-sizeを「CPUコア数×2+ディスクスピンドル数」程度から出発することを推奨しており、仮想スレッド導入後もこの目安は変わりません。増やすべきはスレッド数ではなく、必要であればDBのレプリカ追加やコネクションプーラー(PgBouncerなど)の導入です。
Kubernetesの起動監視は、Webアプリ用とAI推論Pod用でアラートルールを分離します。initialDelaySecondsやfailureThresholdを推論Pod専用のlivenessProbeとして別定義し、モデルロード時間の実測値をもとに閾値を設定し直します。1.35のDRA起動メトリクスが使える環境であれば、これをGrafanaなどのダッシュボードで既存Webアプリと別パネルに分けて可視化すると、誤検知の切り分けがしやすくなります。
まとめ
Java 26とKubernetes 1.35は、既存の業務システムにそのまま乗せると設定の前提のずれから不具合が出やすい組み合わせです。
- JNIとPanamaの混在箇所を
grepで洗い出し、書き換え優先度を決める - コネクションプールのサイズをDBの実処理能力基準で見直す
- AI推論Podの起動監視をWebアプリと別ルールに分離する
まずはjava -versionとHikariCPの設定値、kubectl versionの3点を確認するところから始めると、自分のプロジェクトがどの落とし穴に近いか判断しやすくなります。