Javaの性能 (ジャバのせいのう)では、Javaプラットフォーム の性能について説明する。プログラミング言語 としてのJava に対する批判や、Javaプラットフォームの性能に対する批判は「Javaに対する批判 」の記事を参照のこと。この記事ではJavaプラットフォームの性能について批判以外の説明をする。
プログラミング言語Java は、その「ネットワークから送り込まれるプログラムの安全な実行」や「write once, run anywhere 」というスローガンを、業界にありがちなスローガンだけのスローガンではなく可能な限り達成するべく、Javaバイトコード にコンパイルするコンパイラと、Javaバイトコードを解釈実行するインタプリタであるJava仮想マシン (Java VM, JVM)、という構成の実装を、公式の実装として伴って発表された。
コンピュータ科学 的には特に目新しいものではない。しかし、従来のC言語 あるいはC++ といったネイティブコード にコンパイルする言語で書かれたアプリケーションソフトウェア との性能比較や、当初はJVMのチューニングや高速化手法が進んでいなかったことによる性能の制限、また、当時の一般ユーザーが使っていたMicrosoft Internet Explorer において、Javaアプレット が埋込まれたウェブページを表示しようとすると、JVMを起動するために数十秒から最悪の場合は数分も待たされたことから(起動してしまえば実は高性能なVMだったのだが)、「Javaは遅い」などと言われるようになったため、「Javaの性能」が議論されるようになった。
Javaプログラムの実行速度はJITコンパイル の導入(1997年 / 1998年のJava 1.1 以降)や
[ 1] [ 2]
、
コードの解析の機能が言語に追加されたこと、Java仮想マシン 自体の最適化(2000年からサン・マイクロシステムズ のVMで標準的に動作するようになったHotSpot 技術など)によって大きく向上した。
仮想マシンの最適化手法
オラクル (旧サン・マイクロシステムズ )のJVMの性能は徐々に向上してきたが、このJVMはいくつかの最適化を実装した最初の仮想機械 であることも多く、そうした技術は類似のプラットフォームでも使用されている。
ジャストインタイムコンパイル
初期のJava仮想マシン はバイトコード のインタプリタであり、このことが性能に対する大きな足かせ(平均的なアプリケーションで、Java対Cで10〜20倍程度)になっていた[ 3] 。
Java 1.1で JIT コンパイラが導入された。
Java 1.2でHotSpot と呼ばれる技術が導入された。これは、Java仮想マシン がプログラムの頻繁に実行される箇所、「ホットスポット」の性能解析を実行中に実行し続けるもので、解析した情報は最適化 に利用して、他のパフォーマンスに影響のないコードには余分な負荷をかけることなく、性能を向上させることができる。
Java 1.3でHotSpot が標準で用いられるようになった。
HotSpot技術により、コードはまずインタプリタ実行され、「ホットスポット」が動的にコンパイルされる。Javaの性能測定においてベンチマークをとる前にプログラムを数回実行させる必要があるのはこのためである。
HotSpotによるコンパイルではインライン展開 、ループ展開 、境界チェックの省略 、アーキテクチャ固有のレジスタ割り付け などの様々な最適化手法が用いられる[ 4] 。
ベンチマークによってはこうした手法により10倍の性能向上が見られる[ 5] 。
適応的最適化
適応的最適化 (adaptive optimization ) とは、計算機科学においてプログラムの一部を現在の実行結果のプロファイルに基づき動的再コンパイル (dynamic recompilation ) する技術である。単純な実装では、適応的最適化はジャストインコンパイルとインタプリタ実行を選択するだけだが、より高水準のものでは、データの局所的な状態を用いて分岐を取り除き、インライン展開してコンテキスト切り替えを削減することもできる。
HotSpot のようなJava仮想マシン は、一度JITコンパイル されたコードを解消 (deoptimization : 脱最適化) することも可能である。これによって、積極的な(場合によっては危険な)最適化を実行し、後で最適化を解消し安全な実行方法に戻ることもできる[ 6] [ 7] 。
ガベージコレクション
Java 1.0と1.1のJava仮想マシン では、ガベージコレクション実行後のヒープ が断片化する可能性のあるマーク・アンド・スイープ 方式のガベージコレクション を採用していた。
Java 1.2より、Java仮想マシンは世代別ガベージコレクション を用いるようになり[ 8] 、断片化が起こりづらくなった[ 9] 。
現代的なJava仮想マシンは、ガベージコレクション の性能を改善する様々な手法を用いている[ 10] 。
その他の最適化技術
バイトコード検証の分割
クラス の実行に先立ち、オラクル のJVMはバイトコード の検証を行う。バイトコードのロードと検証は特定のクラスがロードされ実行する準備ができた場合のみ行われ、プログラムの開始時点で行われるわけではない[ 11] 。しかし、Javaのクラスライブラリ も正規のJavaクラスであり、使用時にロードされるため、Javaプログラムの起動時間は例えばC++ のプログラムより長くなることが多い。
分割検証 と呼ばれる技術がJavaプラットフォームのJ2ME で導入され、Java version 6 からJava仮想マシン で利用されるようになった。これはバイトコード の検証を二つのフェーズに分割する[ 12] 。
設計時 - ソースコードからバイトコードにクラスをコンパイルする際
実行時 - クラスをロードする際
こうした手法は、Javaコンパイラがクラスのコードの流れを解析し、コンパイルされたメソッドのバイトコードにクラスのフロー情報の概要を注釈(アノテート)することで動作する。実行時の検証を劇的に単純化するわけではないが、若干の処理を省略することができる。
エスケープ解析と粗粒度ロック
Javaは言語レベルでマルチスレッド に対応している。マルチスレッドとは、下記のようなことを可能にする技術である。
並行コンピューティング - 例えばプログラムがバックグラウンドでタスクを実行中であってもユーザーがGUIを操作できるようにすることで、応答性 やユーザーに与える印象 を改善する
並列コンピューティング - 例えばマルチコア プロセッサのアーキテクチャを活かして、依存関係のない複数の作業を異なるコアで同時に実行し、処理時間 を削減する
しかし、マルチスレッドを使用するプログラムは、スレッド間で共有されるオブジェクト やメソッド 、コードブロック に開発者が特別な注意を払う必要がある。またオブジェクトやコードブロックをロックすることは、それに伴うOSの性質によって時間のかかる操作である(並行性制御 やロックの粒度 を参照)。
Javaライブラリにはどのメソッドが複数のスレッドから使用されるか分からないため、マルチスレッド環境で使用される標準的なライブラリは常にコードブロック のロックを行っている。
Java 6以前では、複数の異なるスレッドが同時にオブジェクトを変更するリスクがない場合でも、仮想マシンはオブジェクトやコードブロックのロックをプログラムの要求にしたがって行っていた(ロックの実装 を参照)。例えば、ローカル変数のVector
があり、それに対する add 操作を行う際、それが確実にローカルでしか使用されずロックが不要である状況でも、同時に他のスレッドから変更されないようロックを行っていた。
public String getNames () {
Vector v = new Vector ();
v . add ( "Me" );
v . add ( "You" );
v . add ( "Her" );
return v . toString ();
}
Java 6では、コードブロックやロックは必要なときだけロックされるようになり[1] [2] 、上記の例では仮想マシンはVector
オブジェクトのロックを行わない。
バージョン 6 Update 14で、Javaは実験的ながらエスケープ解析 をサポートするようになった[3] 。
レジスタ割付の改善
Java 6 以前では、「クライアント」仮想マシンにおけるレジスタ割り付け はかなり初歩的なもので(ブロック を超えてレジスタが生存できない)、これは例えばx86 のようなレジスタ が少ないアーキテクチャ で問題となる。ある操作に必要なレジスタが足りなくなると、コンパイラはレジスタからメモリ(ないしはメモリからレジスタ)に値をコピーするが、メモリは通常レジスタより低速なので、通常より時間がかかる。なお「サーバ」仮想マシンではグラフ彩色 によるレジスタ割り付けを行うため、こうした問題は生じない。
レジスタ割り付けの最適化はサンのJDK 6で導入された[ 13] 。これは同じレジスタを、可能な場合コードブロックをまたがって使用することでメモリアクセスを減らすもので、報告によればいくつかのベンチマークで約60%の性能向上が得られた[ 14] 。
クラスデータの共有
オラクルJVMにおけるクラスデータの共有 (class data sharing: CDS) とは、Javaアプリケーションの起動時間を短くし、同時にメモリ使用量 を削減する仕組みである。JRE がインストールされると、インストーラーはシステムJARファイル (Java のクラスライブラリを全て含むJARファイルで、rt.jarと呼ばれる)からいくつかのクラスを特別な内部表現形式でロードし、この内部表現を「共有書庫」ファイルとして書き出す。それ以降のJVM の呼び出し時には、共有書庫ファイルはメモリマッピング され、これによってクラスをロードする時間を短縮し、複数 JVM プロセスがこれらのクラスのメタデータを共有できるようになる[ 15] 。
起動時間の短縮は、特に小さなプログラムで効果が著しい[ 16] 。
Sun の Java バージョンによる性能の向上
上記以外にも、オラクルのJavaは各バージョンでJava APIの性能向上を多数盛り込んでいる。
JDK 1.1.6
仮想マシンレベルの向上:
J2SE 1.2
仮想マシンレベルの向上:
J2SE 1.3
仮想マシンレベルの向上:
J2SE 1.4
サン・マイクロシステムズが1.3から1.4での性能向上をまとめたリンク を参照
Java SE 5.0
仮想マシンレベルの向上:
Java SE 6
仮想マシンレベルの向上:
その他の向上:
Java SE 6 Update 10
Java Quick Starterにより、OS起動時にJRE のデータをディスクキャッシュにロードしておくことでアプリケーションの起動時間を短縮する[ 21] 。
プラットフォームの一部で、アプリケーションを実行する際に必要な部分もWebからダウンロードされるようになった。JRE全体のサイズは12MBになり、典型的なSwingアプリケーションは4MBしか使用しない。残りはバックグラウンドでダウンロードされる[ 22] 。
Direct3D が標準で使用されるようになり、Windows 上でのグラフィック性能が向上した[ 23] 。複雑なJava 2D の操作を高速化するためGPU のシェーダー を使用するようになった[ 24] 。
今後の性能改善点
今後の性能改善は、Java 6のupdateかJava 7で計画されている[ 25] 。
他の言語との比較
Javaプログラムは通常仮想マシン によって実行時にJITコンパイル されるが、
C/C++のように事前コンパイル することも可能である。JITコンパイルされた場合、その性能は一般的に[4]
プログラムの速度
Javaプログラムの平均的な性能は徐々に向上しており、Javaの性能はC やC++ に匹敵するほどになっており、Javaが低速な場合もあるが、高速な場合もある[ 33] 。2009年3月の時点で、Java は Computer Language Benchmarks Game においてC/C++より5-15%遅い。ベンチマークは小規模で数値演算中心の性能を測定するとも言われる。これは、おそらくCに有利に働く。実際のプログラムでは、JavaがCを上回ったり性能の差はなかったりする。例として、Jake2 (Quake 2 クローンで、GPL のCコードをJavaに変換して作成)のベンチマークがあり、Java 5.0のバージョンは、同じハードウェア構成でCの性能を上回る[ 34] 。データがどのように計測されたか明確ではないが(例えば、オリジナルのQuack 2の実行ファイルが1997年にコンパイルされたものであれば、現在のコンパイラではよりよい最適化を行うことができる)、Javaの同じソースコードがVMを更新するだけで大きく性能向上しており、これは100%静的にコンパイルする方法では達成できない。
また、Javaや類似の言語では可能な最適化で、C/C++では実施できないものもある[ 33] 。
C形式のポインタ は、ポインタをサポートする言語での最適化を困難にする。
コードがプログラムの実行前にコンパイルされるため、#適応的コンパイル は完全にコンパイルされたコードでは実施できず、アーキテクチャの機能や、コードパスを用いた最適化の恩恵を受けられない。いくつかのベンチマークの結果は、C/C++の性能はプロセッサアーキテクチャに対応したコンパイルオプション(たとえばSSE2 の利用など)に強く依存しており、JavaプログラムはJITコンパイル により対象のアーキテクチャに適応できることを示している[ 35] 。
エスケープ解析 の手法は、オブジェクト がどこで使用されるかをコンパイラが知ることができないため、たとえばC++ では使用できない(また、ポインタの使用が原因でもある)。
しかし、JavaとC/C++でのベンチマーク による比較は実施する作業に大きく依存する。例えば、Java 5.0と比較すると、
起動時間
Javaアプリケーションの起動時は、膨大な数のクラス(プラットフォームのクラスライブラリ の全て [要出典 ] のクラスを含む)を使用前にロードしなければならないため、C やC++ よりもかなり時間がかかることが多い。
起動時間の大半はJVMの初期化やクラスのロードそのものではなく、I/Oを伴う操作によるものと思われる [独自研究? ] (rt.jar のクラスデータファイルは40MBあり、JVMは多数のデータをこの巨大なファイルをシークして取り出さなければならない)[ 21] 。いくつかの実験により、バイトコード検証分割 の方法を用いるとクラスのロードは約40%向上するが、大規模なプログラムの起動時間は5%しか向上しないことがわかった[ 45] 。
改善幅は小さいものの、単純な操作を実行しすぐ終了するような小さなプログラムでは、Javaプラットフォームのデータローディングはプログラムの操作の数倍の負荷であるため、向上が目に見えやすい。
Java SE 6 Update 10より、オラクルのJREにはQuick Starterが同梱されるようになり、OSの起動時にクラスのデータをロードしておくことで、ディスクではなくディスクキャッシュからデータを読み出すことができるようになる。
Excelsior JET では、この問題に対して別の方向からアプローチしている。JETのStartup Optimizerはアプリケーションの起動時にディスクから読み出すデータを削減し、さらにシーケンシャルに読み出せるよう配置する。
メモリ使用量
Javaのメモリ使用量はC/C++より大きい。
32ビット環境のJavaでは各オブジェクトに最低8バイト、各配列に12バイトのオーバーヘッドが存在する(64ビットの環境では倍)。また、オブジェクトのサイズが8の倍数でない場合は、8の倍数に切り上げられる。そのため、4バイトの整数を格納するオブジェクトは32ビット環境で16バイト消費する。ただし、C++も仮想関数テーブル を持つ型は、各オブジェクトに32ビット環境で4バイト、64ビット環境で8バイトのポインタ を余分に割り当てる[5] 。また、多重継承 や仮想継承 をするとメンバー関数ポインタ のサイズが増大する処理系もある[ 46] 。
クラスライブラリ が(最低でもプログラムが使用する分は)実行前にロードされていなければならない[6] 。
Javaのバイナリと、ネイティブにJITコンパイルしたものの両方がメモリ上に存在する。
仮想マシン自体がメモリを消費する。
三角関数
Javaの三角関数の性能は、Cと比べて悪い。Javaが数値演算の結果に(使用するハードウェアとも合致しない場合もある)厳密な仕様を定義している[ 47] ためである。
x87 での絶対値
π
{\displaystyle \pi }
/4以上の値に対するサイン、コサインの演算結果は、
π
{\displaystyle \pi }
の値に近似値を用いるため正確ではない[ 48] 。JVMの実装ではソフトウェアで正確な演算を行わなければならず、その領域では大きな性能低下を引き起こす[ 49] 。
Java Native Interface
Java Native Interface はオーバーヘッドが大きく、JVM上で動作するコードとネイティブコードの境界を行き来するコストが高くなっている[ 50] [ 51] 。
ユーザインタフェース
Swing はウィジェット の描画をPure Javaで記述されたJava 2D API に任せているため、ネイティブのウィジェット・ツールキット と比較して低速であるとされてきた。しかし、SwingとOSのネイティブGUIライブラリに描画処理を任せるStandard Widget Toolkit をベンチマークで比較しても、片方が明確に速いわけではなく、結果はコンテキストや環境に大きく依存した[ 52] 。
高性能計算分野での Java の使用
いくつかの独立した研究によれば、高性能計算 (HPC) における Javaの性能は、演算中心のベンチマークでFORTRAN と同等であるが、JVMはグリッドネットワーク 上の通信が多くなると、スケーラビリティに問題があるようである[ 53] 。
しかし、Javaで記述された高性能計算 のアプリケーションがベンチマークで最高の成績を出したことがある。2008年 、Javaで記述されたHPCのプロジェクト Apache Hadoop が、テラバイト級の整数のソートで最高速の結果を出した[ 54] 。
脚注
^ “Symantec's Just-In-Time Java Compiler To Be Integrated Into Sun JDK 1.1 ”. 2009年7月4日閲覧。
^ “Apple Licenses Symantec's Just In Time (JIT) Compiler To Accelerate Mac OS Runtime For Java ”. 2009年7月4日閲覧。
^ Performance Comparison of Java/.NET Runtimes (Oct 2004)
^ Kawaguchi, Kohsuke (2008年3月30日). “Deep dive into assembly code from Java ”. 2008年4月2日閲覧。
^ この記事 によれば、インタプリタモードとHotspotで10倍以上性能が向上している。
^ “The Java HotSpot Virtual Machine, v1.4.1 ”. サン・マイクロシステムズ . 2008年4月20日閲覧。
^ Nutter, Charles (2008年1月28日). “Lang.NET 2008: Day 1 Thoughts ”. 2008年4月20日閲覧。 “Deoptimization is very exciting when dealing with performance concerns, since it means you can make much more aggressive optimizations...knowing you'll be able to fall back on a tried and true safe path later on ”
^ Javaの理論と実践: 1.4.1 JVM中のガーベジ・コレクション
^ Javaの理論と実践: ガベージコレクションとパフォーマンス
^ 例えば、停止時間が大幅に短くなっている。Java で書かれたQuake 2 の例を参照Jake2
^ オラクル以外のJVM、たとえば IBM System i 用のJava/400のように、検証の作業の大半を前もって行い、クラスを検証した情報をキャッシュしておくものもある。
^ New Java SE 6 Feature: Type Checking Verifier at java.net
^ Bug report: new register allocator, fixed in Mustang (JDK 6) b59
^ Mustang's HotSpot Client gets 58% faster! in Osvaldo Pinali Doederlein's Blog at java.net
^ Class Data Sharing at java.sun.com
^ Class Data Sharing in JDK 1.5.0 in Java Buzz Forum at artima developer
^ “Symantec's Just-In-Time Java Compiler To Be Integrated Into Sun JDK 1.1 ”. 2009年7月4日閲覧。
^ “Java gets four times faster with new Symantec just-in-time compiler ”. 2009年7月4日閲覧。
^ STR-Crazier: Performance Improvements in Mustang in Chris Campbell's Blog at java.net[リンク切れ ]
^ See here for a benchmark showing an approximately 60% performance boost from Java 5.0 to 6 for the application JFreeChart
^ a b Haase, Chet (2007年5月). “Consumer JRE: Leaner, Meaner Java Technology ”. サン・マイクロシステムズ. 2007年7月27日閲覧。 “At the OS level, all of these megabytes have to be read from disk, which is a very slow operation. Actually, it's the seek time of the disk that's the killer; reading large files sequentially is relatively fast, but seeking the bits that we actually need is not. So even though we only need a small fraction of the data in these large files for any particular application, the fact that we're seeking all over within the files means that there is plenty of disk activity. ”
^ Haase, Chet (2007年5月). “Consumer JRE: Leaner, Meaner Java Technology ”. サン・マイクロシステムズ. 2007年7月27日閲覧。
^ Haase, Chet (2007年5月). “Consumer JRE: Leaner, Meaner Java Technology ”. サン・マイクロシステムズ. 2007年7月27日閲覧。
^ Campbell, Chris (2007年4月7日). “Faster Java 2D Via Shaders ”. 2008年4月26日閲覧。
^ Haase, Chet (2007年5月). “Consumer JRE: Leaner, Meaner Java Technology ”. サン・マイクロシステムズ. 2007年7月27日閲覧。
^ “JSR 292: Supporting Dynamically Typed Languages on the Java Platform ”. jcp.org. 2008年5月28日閲覧。
^ Goetz, Brian (2008年3月4日). “Java theory and practice: Stick a fork in it, Part 2 ”. 2008年3月9日閲覧。
^ Lorimer, R.J. (2008年3月21日). “Parallelism with Fork/Join in Java 7 ”. infoq.com. 2008年5月28日閲覧。
^ “New Compiler Optimizations in the Java HotSpot Virtual Machine ”. サン・マイクロシステムズ (2006年5月). 2008年5月30日閲覧。
^ Humble, Charles (2008年5月13日). “JavaOne: Garbage First ”. infoq.com. 2008年9月7日閲覧。
^ Coward, Dany (2008年11月12日). “Java VM: Trying a new Garbage Collector for JDK 7 ”. 2008年11月15日閲覧。
^ Python にはPsyco があるが、扱えるコードが限定されており、また Psyco を利用しても Java より性能が低い(リンク 参照)。
^ a b Lewis, J.P.; Neumann, Ulrich. “Performance of Java versus C++ ”. Computer Graphics and Immersive Technology Lab, University of Southern California. 2009年7月4日閲覧。
^ : 260/250 FPS 対245 FPS(ベンチマーク結果 参照)
^ “mandelbrot benchmark ”. Computer Language Benchmarks Game. 2008年2月16日閲覧。
^ “Microbenchmarking C++, C#, and Java: 32-bit integer arithmetic ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: 64-bit double arithmetic ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: File I/O ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Exception ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Single Hash Map ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Multiple Hash Map ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Object creation/ destruction and method call ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Array ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “Microbenchmarking C++, C#, and Java: Trigonometric functions ”. Dr. Dobb's Journal (2005年7月1日). 2007年11月17日閲覧。
^ “How fast is the new verifier? ” (2006年2月7日). 2007年5月9日閲覧。
^ Microsoft Visual C++ など。
^ “Math (Java Platform SE 6) ”. サン・マイクロシステムズ . 2008年6月8日閲覧。
^ Gosling, James (2005年7月27日). “Transcendental Meditation ”. 2008年6月8日閲覧。
^ W. Cowell-Shah, Christopher (2004年1月8日). “Nine Language Performance Round-up: Benchmarking Math & File I/O ”. 2008年6月8日閲覧。
^ Wilson, Steve; Jeff Kesselman (2001年). “JavaTM Platform Performance: Using Native Code ”. サン・マイクロシステムズ . 2008年2月15日閲覧。
^ Kurzyniec, Dawid; Vaidy Sunderam. “Efficient Cooperation between Java and Native Codes - JNI Performance Benchmark ”. 2008年2月15日閲覧。
^ Igor, Kri?nar (2005年5月10日). “SWT Vs. Swing Performance Comparison ”. cosylab.com. 2008年5月24日閲覧。 “Initial expectation before performing this benchmark was to find SWT outperform Swing. This expectation stemmed from greater responsiveness of SWT-based Java applications (e.g., Eclipse IDE) compared to Swing-based applications. However, this expectation could not be quantitatively confirmed. ”
^ Brian Amedro, Vladimir Bodnartchouk, Denis Caromel, Christian Delbe, Fabrice Huet, Guillermo L. Taboada (2008年8月). “Current State of Java for HPC ”. INRIA . 2008年9月4日閲覧。 “We first perform some micro benchmarks for various JVMs, showing the overall good performance for basic arithmetic operations(...). Comparing this implementation with a Fortran/MPI one, we show that they have similar performance on computation intensive benchmarks, but still have scalability issues when performing intensive communications. ”
^ Owen O'Malley - Yahoo! Grid Computing Team (2008年7月). “Apache Hadoop Wins Terabyte Sort Benchmark ”. 2008年12月21日閲覧。 “This is the first time that either a Java or an open source program has won. ”
関連項目
外部リンク
プラットフォーム オラクルのテクノロジー プラットフォーム技術 主なサードパーティ技術 歴史 主要なJVM言語 コミュニティ