[解決済み] Java 8でメソッド参照キャッシュは良いアイデアか?
質問
以下のようなコードがあるとします。
class Foo {
Y func(X x) {...}
void doSomethingWithAFunc(Function<X,Y> f){...}
void hotFunction(){
doSomethingWithAFunc(this::func);
}
}
仮に
hotFunction
が非常に頻繁に呼び出されるとします。その場合、キャッシュすることが望ましいでしょうか。
this::func
をキャッシュすることが望ましいでしょうか。
class Foo {
Function<X,Y> f = this::func;
...
void hotFunction(){
doSomethingWithAFunc(f);
}
}
私がJavaのメソッド参照について理解している限りでは、メソッド参照が使用されるとVirtual Machineは匿名クラスのオブジェクトを作成します。したがって、参照をキャッシュするとそのオブジェクトは一度だけ作成されますが、最初のアプローチでは関数を呼び出すたびにオブジェクトが作成されます。これは正しいのでしょうか?
コード内のホットポジションに現れるメソッド参照はキャッシュされるべきでしょうか、それとも VM はこれを最適化し、キャッシュを不要にすることができるでしょうか。このことについて一般的なベスト プラクティスがあるのか、それともそのようなキャッシュが役に立つかどうかは、高度に VM インプリメンテーションに依存するのでしょうか?
どのように解決するのですか?
同じものを頻繁に実行する場合と、同じものを頻繁に実行する場合を区別する必要があります。 コールサイト を頻繁に実行することと、ステートレスラムダやステートフルラムダで メソッド参照 を頻繁に使用することです。
次の例を見てください。
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=System::gc;
if(r1==null) r1=r2;
else System.out.println(r1==r2? "shared": "unshared");
}
ここでは、同じコールサイトが2回実行され、ステートレスなラムダが生成され、現在の実装では
"shared"
.
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=Runtime.getRuntime()::gc;
if(r1==null) r1=r2;
else {
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
}
}
この2番目の例では、同じコールサイトが2回実行されて
Runtime
インスタンスへの参照を含むラムダを生成し、現在の実装では
"unshared"
と表示されますが
"shared class"
.
Runnable r1=System::gc, r2=System::gc;
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
一方、最後の例では、2つの異なる呼び出し先が同等のメソッド参照を生成していますが、その時点では
1.8.0_05
と表示されます。
"unshared"
そして
"unshared class"
.
ラムダ式やメソッド参照に対して、コンパイラは
invokedynamic
命令で、JRE が提供するクラス内のブートストラップメソッドを参照します。
LambdaMetafactory
と、目的のラムダ実装クラスを生成するために必要な静的引数を参照する命令です。メタファクトリが何を生成するかは実際のJREに委ねられますが、指定された動作として
invokedynamic
命令を記憶し、再利用するために
CallSite
のインスタンスを記憶して再利用します。
現在の JRE が生成する
ConstantCallSite
を含む
MethodHandle
をステートレスラムダ用の定数オブジェクトに変換します (そして、これと異なることをする想像しうる理由はありません)。そしてメソッド参照は
static
メソッドへの参照は常にステートレスです。したがって、ステートレス ラムダと単一のコールサイトに対する答えは、「キャッシュしないで、JVMはそうするでしょう、そうしないなら、あなたが打ち消すべきでない強い理由があるはずです。
パラメータを持つラムダ、および
this::func
への参照を持つラムダは
this
インスタンスへの参照を持つラムダである場合、状況は少し異なります。JRE はそれらをキャッシュすることが許されていますが、これは、ある種の
Map
を維持する必要があり、単純な構造のラムダインスタンスを再び作成するよりもコストがかかる可能性があります。現在の JRE は、状態を持つラムダ インスタンスをキャッシュしません。
しかし、これはラムダクラスが毎回作成されることを意味するものではありません。解決されたコールサイトが、最初の起動時に生成されたラムダクラスのインスタンスを生成する通常のオブジェクト構築のように動作することを意味しています。
同様のことが、異なるコールサイトによって作成された同じターゲットメソッドへのメソッド参照にも当てはまります。JRE はそれらの間で単一のラムダ インスタンスを共有することができますが、現在のバージョンでは、おそらくキャッシュのメンテナンスが報われるかどうかが明確でないため、共有しません。ここでは、生成されたクラスさえも異なるかもしれません。
つまり、あなたの例のようなキャッシュは、そうでない場合とは異なることをプログラムに行わせるかもしれません。しかし、必ずしもより効率的であるとは限りません。キャッシュされたオブジェクトは、一時的なオブジェクトよりも常に効率的とは限りません。ラムダ生成によるパフォーマンスへの影響を本当に測定していない限り、キャッシュを追加すべきではありません。
キャッシュが有効なのは、特殊なケースだけだと思います。
- 同じメソッドを参照する多くの異なるコールサイトについて話している。
-
ラムダはコンストラクタ/クラスの初期化で作成されます。
- 複数のスレッドから同時に呼び出される
- の低い性能に悩まされる。 最初の 呼び出しの
関連
-
ApplicationContextの起動エラーです。条件レポートを表示するには、アプリケーションを'de'で再実行します。
-
ジャバアレイ
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] JavaでNullPointerExceptionを回避する方法
-
[解決済み] JavaにおけるHashMapとHashtableの違いは何ですか?
-
[解決済み] Java Mapの各エントリを効率的に反復処理するには?
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] JavaでArrayListではなくLinkedListを使用するのはいつですか?
-
[解決済み] JavaでStringをintに変換するにはどうしたらいいですか?
-
[解決済み】全てのブラウザで、Webページのキャッシュを制御するには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Eclipse問題 アクセス制限。タイプ 'SunJCE' が API でないことを解決し、/jdk ディレクトリにある /jre と jre の違いについて理解を深める。
-
スレッド "main" での例外 java.lang.ArrayIndexOutOfBoundsException:5 エラー
-
ajax コミット リソースの読み込みに失敗しました: サーバーはステータス 400 で応答しました ()
-
java -jarコマンドでパッケージを実行すると、無効または破損したjarfile xxxx.jarが表示される。
-
Javaジェネリックを1つの記事で
-
ジャバアレイ
-
Javaがテキストファイルを読み込む
-
Javaがリソースリークに遭遇した:'input'が閉じない 解決方法
-
[解決済み】ラムダ式は、実行されるたびにヒープ上にオブジェクトを作成するのか?
-
[解決済み] ラムダ式はコードの行数を節約する以外に使い道があるのか?