まず、javacコンパイラの動作と、JITコンパイラを介したJVMの動作を理解する必要があります。
Runnableはインターフェイスであるため、そのインターフェイスを実装するクラスを作成し、そのインスタンスをThread
コンストラクタに渡すか、匿名内部クラス(AIC)を使用できます。その場合、javac
コンパイラはRunnable
を実装する合成クラスを生成し、インスタンスを作成します。
C++は静的なAOT(事前処理)コンパイルを使用しており、言い換えるとインラインテンプレートを使用できます。 JVMは、適応型のジャストインタイム(JIT)コンパイルを使用します。クラスファイルがロードされると、JVMはコード内にホットスポットがあると判断し、それらをキャッシュ可能なネイティブ命令にコンパイルするまで、バイトコードを解釈します。使用される最適化がどの程度積極的であるかは、使用されているJITに依存します。 OpenJDKには、C1とC2の2つのJIT(クライアントとサーバーと呼ばれることもあります)があります。 C1はコードをより速くコンパイルしますが、最適化はそれほどありません。 C2のコンパイルには時間がかかりますが、より多くの最適化が行われます。コンパイラが最適な最適化を決定した場合は、のrun()
メソッドがインライン展開されます(使用頻度が高い場合)。私たちはAzul(私は彼らのために働いています)は最近、さらに最適化するLLVMに基づいてFalconと呼ばれる新しいJVM JITをリリースしました。
ラムダは少し異なります。どんなラムダ式も同等のAICに変換することができ、JDK 8での早期実装のために、これは構文的な砂糖としてAICに実装された方法です。パフォーマンスを最適化するために、javac
は、代わりにinvokedynamic
バイトコードを使用するコードを生成します。こうすることで、ラムダはクラスファイルでハードコーディングするのではなく、JVMに実装されます。 JVMはAICを使用してもよく、静的メソッドまたは他の実装方法を使用してもよい。軽度の点として、明示的なラムダではなくメソッド参照を使用することは、パフォーマンスのほうがほんのわずかです。
質問のGC面については、コードのプロファイルによって異なります。数百万のRunnable
オブジェクトを使用している場合、私はThread
オブジェクトの影響についてより懸念しています。それらをプールしていない場合、数百万のスレッドを作成して収集するGCのオーバーヘッドは、Runnable
オブジェクトのオーバーヘッドよりはるかに大きくなります。オブジェクトがエデン空間に集められる限り、オーバーヘッドは事実上ゼロです。
他のパフォーマンスの問題に遭遇する可能性は非常に高いので、あまり気にしないでください。 – Kayaman
非同期コードはどのようにインライン化されますか?それがインラインになると、非同期ではありません。これはC++にも当てはまります。エグゼキュータは何を実行するかを知るためにアイデンティティを受け取る必要があります。これは通常C++のメモリ位置です。したがって、非同期ジョブの表現のためにメモリを予約することは避けられません。唯一の例外は、同じ不変の操作を繰り返し実行することであり、その場合、Javaのラムダ式またはメソッド参照は同じオブジェクトに評価されます。 – Holger