You have to make a distinction between frequent executions of the same call-site, for stateless lambda or stateful lambdas, and frequent uses of a method-reference to the same method (by different call-sites).
Look at the following examples:
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");
}
Here, the same call-site is executed two times, producing a stateless lambda and the current implementation will print "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");
}
}
In this second example, the same call-site is executed two times, producing a lambda containing a reference to a Runtime
instance and the current implementation will print "unshared"
but "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");
In contrast, in the last example are two different call-sites producing an equivalent method reference but as of 1.8.0_05
it will print "unshared"
and "unshared class"
.
For each lambda expression or method reference the compiler will emit an invokedynamic
instruction that refers to a JRE provided bootstrap method in the class LambdaMetafactory
and the static arguments necessary to produce the desired lambda implementation class. It is left to the actual JRE what the meta factory produces but it is a specified behavior of the invokedynamic
instruction to remember and re-use the CallSite
instance created on the first invocation.
The current JRE produces a ConstantCallSite
containing a MethodHandle
to a constant object for stateless lambdas (and there’s no imaginable reason to do it differently). And method references to static
method are always stateless. So for stateless lambdas and single call-sites the answer must be: don’t cache, the JVM will do and if it doesn’t, it must have strong reasons that you shouldn’t counteract.
For lambdas having parameters, and this::func
is a lambda that has a reference to the this
instance, things are a bit different. The JRE is allowed to cache them but this would imply maintaining some sort of Map
between actual parameter values and the resulting lambda which could be more costly than just creating that simple structured lambda instance again. The current JRE does not cache lambda instances having a state.
But this does not mean that the lambda class is created every time. It just means that the resolved call-site will behave like an ordinary object construction instantiating the lambda class that has been generated on the first invocation.
Similar things apply to method references to the same target method created by different call-sites. The JRE is allowed to share a single lambda instance between them but in the current version it doesn’t, most probably because it is not clear whether the cache maintenance will pay off. Here, even the generated classes might differ.
So caching like in your example might have your program do different things than without. But not necessarily more efficient. A cached object is not always more efficient than a temporary object. Unless you really measure a performance impact caused by a lambda creation, you should not add any caching.
I think, there are only some special cases where caching might be useful:
- we are talking about lots of different call-sites referring to the same method
- the lambda is created in the constructor/class initialize because later on the use-site will
- be called by multiple threads concurrently
- suffer from the lower performance of the first invocation
invokedynamic
. I doubt that you will see a performance improvement by caching the function object. On the contrary: It might inhibit compiler optimizations. Did you compare the performance of the two variants? – Barnabasinvokedynamic
must be used? I see no reason for this here! – Jussive