Is method reference caching a good idea in Java 8? -
consider have code following:
class foo { y func(x x) {...} void dosomethingwithafunc(function<x,y> f){...} void hotfunction(){ dosomethingwithafunc(this::func); } }
suppose hotfunction
called often. advisable cache this::func
, maybe this:
class foo { function<x,y> f = this::func; ... void hotfunction(){ dosomethingwithafunc(f); } }
as far understanding of java method references goes, virtual machine creates object of anonymous class when method reference used. thus, caching reference create object once while first approach creates on each function call. correct?
should method references appear @ hot positions in code cached or vm able optimize , make caching superfluous? there general best practice or highly vm-implemenation specific whether such caching of use?
you have make distinction between frequent executions of same call-site, stateless lambda or state-full lambdas, , frequent uses of method-reference same method (by different call-sites).
look @ 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, same call-site executed 2 times, producing stateless lambda , current implementation 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 second example, same call-site executed 2 times, producing lambda containing reference runtime
instance , current implementation print "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");
in contrast, in last example 2 different call-sites producing equivalent method reference of 1.8.0_05
print "unshared"
, "unshared class"
.
for each lambda expression or method reference compiler emit invokedynamic
instruction refers jre provided bootstrap method in class lambdametafactory
, static arguments necessary produce desired lambda implementation class. left actual jre meta factory produces specified behavior of invokedynamic
instruction remember , re-use callsite
instance created on first invocation.
the current jre produces constantcallsite
containing methodhandle
constant object stateless lambdas (and there’s no imaginable reason differently). , method references static
method stateless. stateless lambdas , single call-sites answer must be: don’t cache, jvm , if doesn’t, must have strong reasons shouldn’t counteract.
for lambdas having parameters, , this::func
lambda has reference this
instance, things bit different. jre allowed cache them imply maintaining sort of map
between actual parameter values , resulting lambda more costly creating simple structured lambda instance again. current jre not cache lambda instances having state.
but not mean lambda class created every time. means resolved call-site behave ordinary object construction instantiating lambda class has been generated on first invocation.
similar things apply method references same target method created different call-sites. jre allowed share single lambda instance between them in current version doesn’t, because not clear whether cache maintenance pay off. here, generated classes might differ.
so caching in example might have program different things without. not more efficient. cached object not more efficient temporary object. unless measure performance impact caused lambda creation, should not add caching.
i think, there special cases caching might useful:
- we talking lots of different call-sites referring same method
- the lambda created in constructor/class initialize because later on use-site
- be called multiple threads concurrently
- suffer lower performance of first invocation
Comments
Post a Comment