Groovy 1.6引入了Call Site优化。Call Site优化实际上就是方法选择的cache。
方法选择
在静态语言(如Java)中,方法调用的绑定是在编译期完成的(不完全是这样,如虚函数,但总的来说,静态语言的方法调用是非常高效的)。而在动态语言(如Groovy)中,调用的方法是在运行时选择的。这也是造成动态语言比静态语言慢的重要原因之一。
举个例子来说明,譬如要调用“a.call(1)”。
如果是Java的话,在编译期就会选择好调用的方法,在这个例子中,就是选择a对象声明的类(注意,不是a对象真正的类,因为真正的类要到运行时才能知道)中,名字为call、有一个参数、参数类型为int的方法(实际情况要复杂很多,譬如还要考虑boxing和变参等情况),如果找不到的话则编译不通过,否则进行方法绑定。反汇编这个方法调用的话可以看到如“invokevirtual #4; //Method call:(I)V”的指令,表明方法是绑定好的,包括方法名字“call”,参数类型“I”(int),返回值“V”(void)。
如果是Groovy的话,这些都是由Groovy运行时完成的,Groovy对代码进行编译时并不会检查到底有没有一个方法能匹配这个调用。用Groovy 1.5.7进行编译,再反编译为Java代码之后,可以看到如“ScriptBytecodeAdapter.invokeMethodN(class1, a, "call", new Object[] { new Integer(1) })”的语句,由此看出Groovy在编译时并没有真正的选择调用的方法,而是交由运行时决定。
Call Site
根据wikipedia的定义(
http://en.wikipedia.org/wiki/Call_site),Call Site是一行方法的调用,譬如:
a = sqr(b);
c = sqr(b);
是两个Call Site。
Call Site优化
在Groovy 1.6之前,对于同一个Call Site来说,调用该方法n次,则会进行n次的方法选择,譬如:
for (i in 1..3) {
a.call(i)
}
这里,Groovy对call方法就进行了3次的选择,即使3次选择的结果都是一样的。
Groovy 1.6引入的Call Site优化,则是把同一个Call Site的方法选择结果缓存起来,如果下一次调用时的参数类型一样,则调用该缓存起来的方法,否则重新选择。这就是Call Site优化的基本思想。
代码分析
考虑以下的Groovy代码:
class A {
def a() {}
def b() {}
def b(int i) {}
}
class B {
def a = new A()
def c() {
a.a()
d()
}
def d() {
a.a()
a.b()
a.b(1)
}
}
我们先用Groovy 1.6对这段代码进行编译,然后再用jad对编译后的class文件进行反编译。因为A类中的方法都是空的,所以我们只看B类的反编译结果。下面是B类中的c()和d()方法:
public Object c()
{
CallSite acallsite[] = $getCallSiteArray();
acallsite[1].call(a);
return acallsite[2].callCurrent(this);
}
public Object d()
{
CallSite acallsite[] = $getCallSiteArray();
acallsite[3].call(a);
acallsite[4].call(a);
return acallsite[5].call(a, $const$0); // $const$0就是常量1
}
我们来看看$getCallSiteArray():
private static CallSiteArray $createCallSiteArray()
{
return new CallSiteArray($ownClass, new String[] {
"<$constructor$>", "a", "d", "a", "b", "b" // 每个Call Site的方法名字
});
}
private static CallSite[] $getCallSiteArray()
{
CallSiteArray callsitearray;
if($callSiteArray == null || (callsitearray = (CallSiteArray)$callSiteArray.get()) == null)
{
callsitearray = $createCallSiteArray();
$callSiteArray = new SoftReference(callsitearray);
}
return callsitearray.array;
}
$getCallSiteArray()实际上就是对$callSiteArray的lazy创建。
我们可以看到,“acallsite[1].call(a);”就是对方法名为"a"的CallSite进行调用,而“acallsite[2].callCurrent(this);”则是对方法名为“d”的CallSite进行调用,如此类推。
我们再来看看CallSiteArray的构造函数里做些什么:
public CallSiteArray(Class owner, String [] names) {
this.owner = owner;
array = new CallSite[names.length];
for (int i = 0; i < array.length; i++) {
array[i] = new AbstractCallSite(this, i, names[i]);
}
}
所以,第一次调用“acallsite[1].call(a);“时,就是调用AbstractCallSite类的call方法。下面是该方法的代码:
public Object call(Object receiver, Object[] args) throws Throwable {
return CallSiteArray.defaultCall(this, receiver, args);
}
再看看CallSiteArray.defaultCall()的代码:
public static Object defaultCall(CallSite callSite, Object receiver, Object[] args) throws Throwable {
return createCallSite(callSite, receiver, args).call(receiver, args);
}
...
private static CallSite createCallSite(CallSite callSite, Object receiver, Object[] args) {
CallSite site;
if (receiver == null)
return new NullCallSite(callSite);
if (receiver instanceof Class)
site = createCallStaticSite(callSite, (Class) receiver, args);
else if (receiver instanceof GroovyObject) {
site = createPogoSite(callSite, receiver, args); // 我们只考虑这种情况
} else {
site = createPojoSite(callSite, receiver, args);
}
replaceCallSite(callSite, site); // 替换CallSite
return site;
}
private static void replaceCallSite(CallSite oldSite, CallSite newSite) {
oldSite.getArray().array [oldSite.getIndex()] = newSite;
}
可以看到createCallSite()最后通过调用replaceCallSite()把旧的CallSite替换为新的CallSite,因此第二次调用“acallsite[1].call(a);”时就是直接调用新的CallSite,也就是说该CallSite被缓存起来了。
我们在这里只考虑POGO的情况,即createPogoSite()方法。而POJO的情况稍微复杂一点,因为涉及到POJO per-instance metaclass的情况(我将在下一篇文章中分析它的实现)。下面是createPogoSite()的代码:
private static CallSite createPogoSite(CallSite callSite, Object receiver, Object[] args) {
if (receiver instanceof GroovyInterceptable)
return new PogoInterceptableSite(callSite);
MetaClass metaClass = ((GroovyObject)receiver).getMetaClass();
if (metaClass instanceof MetaClassImpl) {
return ((MetaClassImpl)metaClass).createPogoCallSite(callSite, args); // 我们只考虑这种情况
}
return new PogoMetaClassSite(callSite, metaClass);
}
我们只考虑对象的metaclass是MetaClassImpl的情况(这也是Groovy对象的默认情况)。下面是MetaClassImpl.createPogoCallSite()的代码:
public CallSite createPogoCallSite(CallSite site, Object[] args) {
if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
Class [] params = MetaClassHelper.convertToTypeArray(args); // 获取参数的类型
MetaMethod metaMethod = getMethodWithCachingInternal(theClass, site, params); // 选择方法
if (metaMethod != null)
return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args); // 如果找到匹配的方法,则创建一个PogoMetaMethodSite,并把找到的方法绑定其中
}
return new PogoMetaClassSite(site, this); //否则创建一个PogoMetaClassSite
}
PogoMetaMethodSite.createPogoMetaMethodSite()就是用来根据不同的情况创建PogoMetaMethodSite或它的子类的一个实例。我们最后来看看PogoMetaMethodSite.call()方法:
public Object call(Object receiver, Object[] args) throws Throwable {
if(checkCall(receiver, args)) { // 如果参数类型相同,则调用绑定的方法
try {
return invoke(receiver,args); // 调用绑定的方法
} catch (GroovyRuntimeException gre) {
throw ScriptBytecodeAdapter.unwrap(gre);
}
} else { // 否则创建新的CallSite,即再次进行方法查找
return CallSiteArray.defaultCall(this, receiver, args);
}
}
protected boolean checkCall(Object receiver, Object[] args) {
try {
return usage.get() == 0
&& ((GroovyObject)receiver).getMetaClass() == metaClass // metaClass still be valid
&& MetaClassHelper.sameClasses(params, args); // 检查参数类型是否一样
}
catch (NullPointerException e) {
if (receiver == null)
return false;
throw e;
}
catch (ClassCastException e) {
if (!(receiver instanceof GroovyObject))
return false;
throw e;
}
}
最后,我们来再次总结这个过程:
第一次调用“acallsite[1].call(a)“时,通过CallSiteArray.createCallSite()方法创建了PogoMetaMethodSite类的一个新CallSite,并把默认的AbstractCallSite覆盖掉。在创建PogoMetaMethodSite的过程中,将进行方法的选择,并把找到的方法绑定到PogoMetaMethodSite中。最后就是调用该方法:
当第二次调用“acallsite[1].call(a)“时,就是直接调用PogoMetaMethodSite.call(),这时候PogoMetaMethodSite.call()就会检查传入的参数类型是否与绑定的方法(即上次找到的方法)的参数类型相同,相同则调用该绑定的方法,否则将再次调用CallSiteArray.createCallSite()方法,创建一个新的CallSite对象,并重新进行方法选择。
除了普通的方法调用的情况外,还有调用当前对象方法、获取/设置属性、调用构造函数、调用静态函数的情况,在此不再做详细分析,有兴趣的可以直接查阅Groovy的源代码。
以上分析有不当之处敬请指出,谢谢大家的阅读。
分享到:
相关推荐
0,下载Grails( http://dist.codehaus.org/grails/grails-bin-0.4.2.zip ,请留意朝花夕拾——Groovy & Grails中的“最新版本”提示)并解压到自己指定位置(我的位置是D:\D\MY_DEV\grails) 1,设置环境变量GRAILS...
NULL 博文链接:https://key232323.iteye.com/blog/1336400
scala erlang groovy python 原理 比较 分析
groovy环境的mock工具,用于辅助单元测试.
赠送jar包:groovy-3.0.9.jar; 赠送原API文档:groovy-3.0.9-javadoc.jar; 赠送源代码:groovy-3.0.9-sources.jar; 赠送Maven依赖信息文件:groovy-3.0.9.pom; 包含翻译后的API文档:groovy-3.0.9-javadoc-API...
apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望大家多多下载,apache-groovy-3.0.8.zip apache官网的groovy3.0.8版本,希望...
groovy入门经典,groovyeclipse 插件
Making Java Groovy is a practical handbook for developers who want to blend Groovy into their day to day work with Java It starts by introducing the key differences between Java and Groovy and how you...
Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署
Groovy轻松入门—搭建Groovy开发环境 Groovy轻松入门—搭建Groovy开发环境
groovy-2.3.6-installer windows安装版本
Groovy
groovy
什么是 Groovy? Groovy 是 JVM 的一个替代语言 — 替代 是指可以用 Groovy 在 Java 平台上进行 Java 编程,使用方式基本与使用 Java 代码的方式相同。在编写新应用程序时,Groovy 代码能够与 Java 代码很好地结合,...
The Groovy web site (http://groovy.codehaus.org) gives one of the best definitions of Groovy: “Groovy is an agile dynamic language for the Java Platform with many features that are inspired by ...
Groovy in Action, Second Edition is the undisputed definitive reference on the Groovy language. Written by core members of the Groovy language team, this book presents Groovy like no other can—from ...
groovy eclipse plugin 2.9.0 ,groovy eclipse 插件 2.9.0
groovy eclipse plugin2groovy eclipse plugin2groovy eclipse plugin2groovy eclipse plugin2groovy eclipse plugin2groovy eclipse plugin2groovy eclipse plugin2
Groovy jar包 3.0.