- 浏览: 3052975 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
(注意:这篇blog里的信息只描述了发文时的情况。到JDK7正式发布的时候已经这篇文章里介绍的部分内容已经过时,包括MethodHandle的调用语法、java.dyn包改名为java.lang.invoke、该包的里API等。下面内容并未更新,敬请理解。)
JDK 7将会实现JSR 292,为在JVM上实现动态语言提供更多支持。其中,MethodHandle是JSR 292的重要组成部分之一。有了它,意味着Java终于有了引用方法的方式,或者用C的术语说,“函数指针”。(我差点要说“引用‘方法’的‘方法’”了,好pun)。
下面的讨论都是基于当前(2009-09)的设计而进行的,今后相关具体设计可能变化,但大的方向应该比较明确了。JDK 7的代码例子都是在JDK 7 Binary Snapshot build 70下测试的。执行程序时要添加-XX:+EnableMethodHandles参数。
与其说JDK 7的MethodHandle像C的函数指针,还不如说像.NET的委托。
C#与.NET从1.0版开始就有“委托”的概念,通过委托可以在代码中引用任意方法,无论方法的可访问性、所属类型如何,无论是静态还是实例方法。之前一帖也提到了,.NET的委托提供了为方法创建“别名”的能力,使我们可以用统一的方式去调用签名相同但名字和所属类型都不一定相同的方法。与C的函数指针所不同的是,.NET的委托不但引用了方法,还会引用执行该方法所需要的环境,对引用实例方法的委托来说“环境”就是方法所在的实例;而C的函数指针则仅指向函数的代码而已,没有引用环境的功能。而且,.NET的委托包含了足够的元数据,可用于运行时做类型检查;而C的函数指针则仅仅是个裸指针。调用委托的速度接近调用虚方法的速度。
JDK 7将引入的MethodHandle从许多方面说都与.NET的委托非常相似。MethodHandle也可以指向任意方法,提供为方法创建“别名”的能力;可以用统一的方式去调用MethodHandle。此外,MethodHandle还支持组合,可以以适配器的方式将多个MethodHandle串在一起,实现参数过滤、参数转换、返回值转换等许多功能。调用MethodHandle的速度接近调用接口方法的速度。
MethodHandle对许多JVM的内部实现来说并不是一个全新的概念。要实现JVM,在内部总会保留一些指向方法的指针。JDK 7只是把它(和其它许多JVM里原本就支持的概念)具体化为Java类型暴露给Java代码用而已;这就是所谓的“reification”。
好吧,简单介绍了些背景,下面就通过代码来认识和感受一下JDK 7的MethodHandle,并与.NET的委托对比。
第一组例子,照例上hello world:
JDK 7:
执行这个测试需要使用如下命令:
(注意“java”要使用JDK 7的,不要用了JDK 6或更早的)
首先,要使用MethodHandle,需要引入的类型都在java.dyn包里。这个例子用到的是MethodHandles、MethodHandles.Lookup、MethodType、MethodHandle几个。
流程是:
0、调用MethodHandles.lookup()方法,遍历调用栈检查访问权限,然后得到一个MethodHandles.Lookup实例;该对象用于确认创建MethodHandle的实例的类对目标方法的访问权限是否满足要求,并提供搜索目标方法的逻辑;
1、指定目标方法的“方法类型”,得到一个MethodType实例;
2、通过MethodHandles.lookup()静态方法得到一个类型为MethodHandles.Lookup的工厂,然后靠它搜索指定的类型、指定的名字、指定的方法类型的方法,得到一个MethodHandle实例;
3、调用MethodHandle上的invoke方法。
其中,第1步中调用的MethodType.make()方法接收的参数是一组类型,第一个参数是返回类型,后面依次是各个参数的类型。上例中MethodType.make(void.class)得到的就是一个返回类型为void,参数列表为空的方法类型。如果熟悉Java字节码的话,这个方法类型的描述符就是()V。关于方法描述符的格式,可以参考JVM规范第二版4.3.3小节。MethodType的实例只代表所有返回值与参数类型匹配的一类方法的方法类型,自身没有名字;在检查某个方法是否与某个MethodType匹配时只考虑结构,可以算是一种特殊的structural-typing。
第2步看起来跟普通的反射很像,但通过反射得到的代表方法的对象是java.lang.reflect.Method的实例,它含有许多跟“执行”没有直接关系的信息,比较笨重;通过Method对象调用方法只是正常方法调用的模拟,所有参数会被包装为一个数组,开销较大。而MethodHandle则是个非常轻量的对象,主要目的就是用来引用方法并调用;通过它去调用方法不会导致参数被包装,原始类型的参数也不会被自动装箱。
MethodHandles.Lookup上有三个find方法,包括findStatic、findVirtual、findSpecial,分别对应invokestatic、invokevirtual/invokeinterface、invokespecial会对应的调用逻辑。注意到findVirtual方法所返回的MethodHandle的方法类型会包含一个显式的“this”参数作为第一个参数;调用这样的MethodHandle要显式传入“receiver”。这个看起来就跟.NET的开放委托相似,可以参考我之前的一帖。由于JDK 7的MethodHandle支持currying,可以把receiver保存在MethodHandle里,所以也可以创建出类似.NET的闭合委托的MethodHandle实例。
MethodHandles.Lookup上还有一组方法可以从通过反射API得到的Constructor、Field或Method对象创建出对应的MethodHandle。
第3步调用的MethodHandle.invoke()看似是一个虚方法,实际上并不是MethodHandle上真的存在的方法,而只是标记用的虚构出来的方法。上例中第13行对应的Java字节码是:
也就是假装MethodHandle上有一个描述符为()V且名为invoke的虚方法,通过invokevirtual指令去调用它。
Java编译器为它做特殊处理:返回值类型如同泛型参数在<>内指定,不写的话默认为返回Object类型;参数列表的类型则由Java编译器根据实际参数的表达式推断出来。与正常的泛型方法不同,MethodHandle.invoke指定返回值类型可以使用void和所有原始类型,不必像使用泛型方法时需要把原始类型写为对应的包装类型。
MethodHandle的方法类型不是Java语言的静态类型系统的一部分。虽然它的实例在运行时带有方法类型信息(MethodType),但在编译时Java编译器却不知道这一点。所以在编译时,调用invoke时传入任意个数、任意类型的参数都可以通过编译;但在运行时要成功调用,由Java编译器推断出来的返回值类型与参数列表必须与运行时MethodHandle实际的方法类型一致,否则会抛出WrongMethodTypeException。John Rose把MethodHandle.invoke的多态性称为“签名多态性(signature polymorphism)”。
用户可以自行继承java.dyn.JavaMethodHandle来创建自定义的MethodHandle子类,可以添加域或方法等,并可以指定该类型看作MethodHandle时的“入口点”——实际指向的方法。
许多JVM实现在JIT编译的时候会做激进的优化,包括常量传播、内联、逃逸分析、无用代码削除等许多。JDK 7的MethodHandle的一个好处是它就像它所指向的目标方法的替身一样,JVM原本可以做的优化对MethodHandle也一样支持,特别是有需要的时候可以把目标方法内联到调用处。相比之下,通过反射去调用方法则无法被JVM有效的优化。
对比C#的例子:
C# 2.0:
这段代码与前面Java版的TestMethodHandle1功能基本相同。来观察一下两者的异同点。
要在C#里使用委托的流程是:
0、事先声明好合适的委托类型;
1、适用合适的委托类型,指定目标方法创建出委托的实例;
2、调用委托上的Invoke()方法。
.NET允许用户自定义委托类型,在C#里的语法是:
该语法与方法的声明语法非常像,只是在返回类型之前多了个delegate关键字而已,比C中typedef函数指针类型的语法容易多了。上例用到的System.Action类型就是标准库里声明好的一个委托类型,其声明形如:
这样声明出来的委托类型相当于声明了一个Action类,继承System.MulticastDelegate,并且拥有一个返回值类型为void,参数列表为空的Invoke()方法。在C#里,用户无法像声明普通类型一样通过声明一个继承System.Delegate或System.MulticastDelegate的类来得到一个新的委托类型,而只能用上述语法来声明。不过从CLR的角度看,并没有限制用户不能自行继承上述两种类型来声明新的委托类型。
委托是.NET类型系统的一部分。两个委托类型即便表示的签名一致也会被认为是不同的类型,不能相互赋值/转换。这体现出了C#与.NET类型的nominal-typing性质。创建委托实例时则只考虑目标方法与委托类型在签名上是否吻合,而不考虑名字问题,这点又与JDK 7的MethodHandle相似。
委托上的Invoke方法的签名与委托声明的相吻合。在编译时,调用委托的Invoke()方法与调用一般的虚方法一样会被类型检查。
目前在C#里可以显式调用委托上的Invoke()方法,也可以直接把委托当成方法用括号调用。是显式调用Invoke()还是直接用括号调用委托,现在来说只是程序员的偏好问题而已。事实上在C# 2.0以前编译器会阻止程序员显式调用Invoke()方法。
用C#代码例子再稍微解释一下:
Java版例子中,创建MethodHandle对象需要在代码里通过MethodHandles.Lookup工厂来查找目标;在C#里编译器已经帮忙找出了目标方法的token,写起来方便许多;C#编译器会根据方法名与委托的签名去寻找合适重载版本的方法,找出它的token,并用于创建委托实例。如果目标方法无法在编译时确定,使用System.Delegate.CreateDelegate(Type, MethodInfo)方法也可以依靠反射信息创建出委托。
CLI的几个主流实现,微软的CLR、Novell的Mono等的JIT编译器都比较静态,遇到虚方法和委托调用都不会内联。从这点说,.NET比JVM的技术复杂度要低一些。不过至少在.NET里使用委托不会带来多少额外开销,所以还是可以放心使用的。
第二组例子,给hello world添加参数:
JDK 7:
编译,以下述命令运行
输出结果为:
基本上跟第一组例子一样,只是让hello()多了个参数而已。留意一下创建MethodType实例的代码如何对应的改变。
第15行对应的Java字节码是:
留意Java编译器是如何根据调用invoke时传入的参数的静态类型(编译时类型)来决定invoke的方法描述符。(Ljava/lang/String;)V的意思是返回值类型为void,参数列表有一个参数,类型为java.lang.String。
如果把代码稍微修改,使MethodHandle的方法类型与Java编译器推断的调用类型不相符的话:
编译运行会看到:
这演示了Java编译器将invoke的方法类型推断为(Ljava/lang/String;)V,而被调用的MethodHandle实例实际的方法类型却是(Ljava/lang/Object;)V,JVM便认为这个调用不匹配并拒绝执行。关键点是:调用invoke时,参数表达式的静态类型(编译时类型)必须与MethodHandle的方法类型中对于位置的参数类型“准确一致”;虽然String类型的引用可以隐式转换为Object类型的,但不满足“准确一致”的要求。
要想让修改过的TestMethodHandle2再次正确运行,可以把第15行改为:method.<void>invoke((Object)args[0]);,也就是加个类型转换,使Java编译器推断出来的方法描述符为(Ljava/lang/Object;)V。或者也可以加一个适配器:
这里演示了MethodHandle的可组装性:通过给实际调用目标装一个转换参数类型的适配器,方法调用就又可以成功了。
对比C#的例子:
C# 3.0:
基本上跟第一组例子也是一样的。Action<T>是标准库里预先声明好的一个泛型委托类型,其声明形如:
为了对比,下面也把Hello()的参数类型改为object,
编译和运行都没有任何问题。这里要演示的是Invoke()方法是有确定的签名的,与委托类型声明的相吻合。编译器不会擅自推断Invoke()的签名。
第三组例子,可指定排序条件的快速排序:
前面一直在用hello world作例子或许是无聊了点,下面弄点稍微长一些的。
JDK 7:
编译,运行,输出结果为:
核心的sort()方法是个简单的快排实现。为了让用户能够指定排序条件,我让它接收MethodHandle为参数来提供判断逻辑。在没有MethodHandle之前,我可能会选择使用策略模式来达到剥离出部分算法的目的。JDK里的许多API也是这么做的,例如Arrays.sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c),其中的Comparator参数就是“策略对象”。现在就可以直接用一个MethodHandle来代替它了。
例子的整体结构跟前两组没有显著的不同,应该还是比较好理解的。
值得注意的是,我把swap()写为了泛型方法,方便以后再更多地方能复用。排序也应该是个通用算法,为何不把sort()也写为泛型呢?
如果把例子中sort()方法里出现的String全部直接替换为泛型的,变成:
编译没问题,但运行的时候会看到comparer.<int>invoke(...)的调用抛WrongMethodTypeException异常。你可能会纳闷:array数组的元素类型不是E么,是泛型的啊;以String为泛型参数调用sort()的时候,invoke的实际参数类型与comparer的方法类型应该匹配才对,怎么会出错呢?
问题就在于Java的泛型是通过类型擦除法(type-erasure)来实现的,编译过后所有泛型参数都变为了Object,这里也不例外。所以Java编译器推断出来的invoke()的描述符是(Ljava/lang/Object;Ljava/lang/Object;)I,这就出现我们在第二组例子里遇到的问题了——方法类型不准确匹配。
于是解决的办法也很简单,只要改成这样:
在调用核心的sort()方法前,先加一个适配器来解决类型差异的问题,就万事大吉了 ^ ^
对比C#的例子:
C# 2.0
基本上也没什么需要特别解释的了。System.Comparison<T>是标准库里预先声明的一个泛型委托类型,其声明形如:
注意.NET里委托与泛型可以很好的结合在一起使用,而不必考虑转换参数类型的问题,因为.NET的泛型信息会带到运行时。或者说,“.NET generics are reified”。
如果用上C# 3.0和.NET Framework 3.5的新特性,使用lambda表达式来提供排序依据,
我们就不必再为例中用到的排序条件写具名方法了。实际上C#编译器仍然为这两个lambda表达式生成了私有静态方法,我们得到的东西是一样的(硬要说的话,少得到了俩名字),但需要写在代码里的东西减少了。
如果进一步改为使用标准库中现成的方法来排序,
整个代码就简洁了许多。
Alright,这帖就介绍MethodHandle与.NET的委托到这里。希望上面的例子达到了简单介绍JDK 7的MethodHandle的目的。
你可能会心存疑惑,“我为什么需要引用别的方法呢?” 由于Java以前一直没有提供这样的功能,一直只使用Java的人可能较少思考这个问题。但你可能会碰到过这样的问题,有时候要调用的目标只有到了运行的时候才知道,无法在代码里直接写方法调用,那怎么办?以前的解决办法就只有通过反射了。一个最简单的使用场景就是,原本通过反射做的方法调用,现在都可以通过MethodHandle完成。如果一个方法频繁被反射调用,开销会很明显,而且难以优化;通过MethodHandle调用则跟正常的接口方法调用速度接近,没有各种包装/装箱的开销,而且可以被JVM优化,何乐而不为?
在MethodHandle出现之前,许多JVM上的脚本语言实现,如JRuby,为了提高调用方法的速度,选择生成大量很小的“invoker类”来对方法签名做特化,通过它们去调用目标方法,避免反射调用的开销。但这么做有许多问题,一是实现麻烦,而是对PermGen堆带来巨大的压力。MethodHandle的出现极大的改善了状况,既便于使用,效率又高,而且还不会对PermGen带来多少压力。Charles Nutter在一帖中描述了这个问题。
(编辑:经指正,Groovy当前还没有用invoke stub,只是做了callsite caching优化。我只读过Groovy的编译器的代码,没读过运行时部分的代码,忽悠了同学们了,抱歉 <(_ _)>)
进一步介绍等以后写invokedynamic的时候再写了~ until then
Have fun ^ ^
MethodHandle是JSR 292的一部分,而JSR 292主要服务的对象是JVM上的动态语言实现。能为Java带来好处算是个不错的副作用。
正是如此。Charles Nutter基于invokedynamic实现了个小的动态语言,目前已经比JRuby快了。大家都很期待自己的语言能从invokedynamic挖出多少性能潜力来 ^ ^
有点同感
文章中说了,作为重量级的method reflection的替代品还是不错的。比之策略模式来说,这个method handler的调用方式还是比较恶心。
嗯,没错。说真的,由于缺乏在代码中直接表示对方法的引用的语法,要用MethodHandle去替代策略模式挺丑的。在需要“传递一小块代码”的场景中我可能还是会偏向使用匿名内部类,其实也不麻烦。
MethodHandle的主要要解决的问题就是“要高效的调用编译时已知签名但具体目标未知的方法”。可以想像有时候要根据配置去找bean上的getter/setter,或者是要不通过继承而通过聚合去拦截方法,之类的;这种地方以前只能靠反射,如今MethodHandle提供了更轻量且高效的解决方案。
我在这帖里故意没强调MethodHandle跟invokedynamic的关系,因为还有后续的帖在草稿箱里窝着 =v= 我原本是先在记invokedynamic的资料,写着写着发现不先写MethodHandle不行……||||
还是蛮实用的吧. 虽然不能代替reflection .但是提高了调用效率 ..
话说reflection 本来在一般系统中用的就不多... method handle会不会在编程范式上有一些新的东西出现呢?
老赵或许可以从c#的委托中预见到将来Java可能会出现的范式. . 跟fx在这里展开谈谈吧.. 洗耳恭听.
文章中说了,作为重量级的method reflection的替代品还是不错的。比之策略模式来说,这个method handler的调用方式还是比较恶心。
JDK 7将会实现JSR 292,为在JVM上实现动态语言提供更多支持。其中,MethodHandle是JSR 292的重要组成部分之一。有了它,意味着Java终于有了引用方法的方式,或者用C的术语说,“函数指针”。(我差点要说“引用‘方法’的‘方法’”了,好pun)。
下面的讨论都是基于当前(2009-09)的设计而进行的,今后相关具体设计可能变化,但大的方向应该比较明确了。JDK 7的代码例子都是在JDK 7 Binary Snapshot build 70下测试的。执行程序时要添加-XX:+EnableMethodHandles参数。
与其说JDK 7的MethodHandle像C的函数指针,还不如说像.NET的委托。
C#与.NET从1.0版开始就有“委托”的概念,通过委托可以在代码中引用任意方法,无论方法的可访问性、所属类型如何,无论是静态还是实例方法。之前一帖也提到了,.NET的委托提供了为方法创建“别名”的能力,使我们可以用统一的方式去调用签名相同但名字和所属类型都不一定相同的方法。与C的函数指针所不同的是,.NET的委托不但引用了方法,还会引用执行该方法所需要的环境,对引用实例方法的委托来说“环境”就是方法所在的实例;而C的函数指针则仅指向函数的代码而已,没有引用环境的功能。而且,.NET的委托包含了足够的元数据,可用于运行时做类型检查;而C的函数指针则仅仅是个裸指针。调用委托的速度接近调用虚方法的速度。
JDK 7将引入的MethodHandle从许多方面说都与.NET的委托非常相似。MethodHandle也可以指向任意方法,提供为方法创建“别名”的能力;可以用统一的方式去调用MethodHandle。此外,MethodHandle还支持组合,可以以适配器的方式将多个MethodHandle串在一起,实现参数过滤、参数转换、返回值转换等许多功能。调用MethodHandle的速度接近调用接口方法的速度。
MethodHandle对许多JVM的内部实现来说并不是一个全新的概念。要实现JVM,在内部总会保留一些指向方法的指针。JDK 7只是把它(和其它许多JVM里原本就支持的概念)具体化为Java类型暴露给Java代码用而已;这就是所谓的“reification”。
好吧,简单介绍了些背景,下面就通过代码来认识和感受一下JDK 7的MethodHandle,并与.NET的委托对比。
第一组例子,照例上hello world:
JDK 7:
import java.dyn.*; import static java.dyn.MethodHandles.*; public class TestMethodHandle1 { private static void hello() { System.out.println("Hello world!"); } public static void main(String[] args) { MethodType type = MethodType.make(void.class); MethodHandle method = lookup() .findStatic(TestMethodHandle1.class, "hello", type); method.<void>invoke(); } }
执行这个测试需要使用如下命令:
java -XX:+EnableMethodHandles TestMethodHandle1
(注意“java”要使用JDK 7的,不要用了JDK 6或更早的)
首先,要使用MethodHandle,需要引入的类型都在java.dyn包里。这个例子用到的是MethodHandles、MethodHandles.Lookup、MethodType、MethodHandle几个。
流程是:
0、调用MethodHandles.lookup()方法,遍历调用栈检查访问权限,然后得到一个MethodHandles.Lookup实例;该对象用于确认创建MethodHandle的实例的类对目标方法的访问权限是否满足要求,并提供搜索目标方法的逻辑;
1、指定目标方法的“方法类型”,得到一个MethodType实例;
2、通过MethodHandles.lookup()静态方法得到一个类型为MethodHandles.Lookup的工厂,然后靠它搜索指定的类型、指定的名字、指定的方法类型的方法,得到一个MethodHandle实例;
3、调用MethodHandle上的invoke方法。
其中,第1步中调用的MethodType.make()方法接收的参数是一组类型,第一个参数是返回类型,后面依次是各个参数的类型。上例中MethodType.make(void.class)得到的就是一个返回类型为void,参数列表为空的方法类型。如果熟悉Java字节码的话,这个方法类型的描述符就是()V。关于方法描述符的格式,可以参考JVM规范第二版4.3.3小节。MethodType的实例只代表所有返回值与参数类型匹配的一类方法的方法类型,自身没有名字;在检查某个方法是否与某个MethodType匹配时只考虑结构,可以算是一种特殊的structural-typing。
第2步看起来跟普通的反射很像,但通过反射得到的代表方法的对象是java.lang.reflect.Method的实例,它含有许多跟“执行”没有直接关系的信息,比较笨重;通过Method对象调用方法只是正常方法调用的模拟,所有参数会被包装为一个数组,开销较大。而MethodHandle则是个非常轻量的对象,主要目的就是用来引用方法并调用;通过它去调用方法不会导致参数被包装,原始类型的参数也不会被自动装箱。
MethodHandles.Lookup上有三个find方法,包括findStatic、findVirtual、findSpecial,分别对应invokestatic、invokevirtual/invokeinterface、invokespecial会对应的调用逻辑。注意到findVirtual方法所返回的MethodHandle的方法类型会包含一个显式的“this”参数作为第一个参数;调用这样的MethodHandle要显式传入“receiver”。这个看起来就跟.NET的开放委托相似,可以参考我之前的一帖。由于JDK 7的MethodHandle支持currying,可以把receiver保存在MethodHandle里,所以也可以创建出类似.NET的闭合委托的MethodHandle实例。
MethodHandles.Lookup上还有一组方法可以从通过反射API得到的Constructor、Field或Method对象创建出对应的MethodHandle。
第3步调用的MethodHandle.invoke()看似是一个虚方法,实际上并不是MethodHandle上真的存在的方法,而只是标记用的虚构出来的方法。上例中第13行对应的Java字节码是:
invokevirtual java/dyn/MethodHandle.invoke:()V
也就是假装MethodHandle上有一个描述符为()V且名为invoke的虚方法,通过invokevirtual指令去调用它。
Java编译器为它做特殊处理:返回值类型如同泛型参数在<>内指定,不写的话默认为返回Object类型;参数列表的类型则由Java编译器根据实际参数的表达式推断出来。与正常的泛型方法不同,MethodHandle.invoke指定返回值类型可以使用void和所有原始类型,不必像使用泛型方法时需要把原始类型写为对应的包装类型。
MethodHandle的方法类型不是Java语言的静态类型系统的一部分。虽然它的实例在运行时带有方法类型信息(MethodType),但在编译时Java编译器却不知道这一点。所以在编译时,调用invoke时传入任意个数、任意类型的参数都可以通过编译;但在运行时要成功调用,由Java编译器推断出来的返回值类型与参数列表必须与运行时MethodHandle实际的方法类型一致,否则会抛出WrongMethodTypeException。John Rose把MethodHandle.invoke的多态性称为“签名多态性(signature polymorphism)”。
用户可以自行继承java.dyn.JavaMethodHandle来创建自定义的MethodHandle子类,可以添加域或方法等,并可以指定该类型看作MethodHandle时的“入口点”——实际指向的方法。
许多JVM实现在JIT编译的时候会做激进的优化,包括常量传播、内联、逃逸分析、无用代码削除等许多。JDK 7的MethodHandle的一个好处是它就像它所指向的目标方法的替身一样,JVM原本可以做的优化对MethodHandle也一样支持,特别是有需要的时候可以把目标方法内联到调用处。相比之下,通过反射去调用方法则无法被JVM有效的优化。
对比C#的例子:
C# 2.0:
using System; static class TestDelegate1 { static void Hello() { Console.WriteLine("Hello world!"); } static void Main(string[] args) { Action method = Hello; // Action method = new Action(Hello); method(); // method.Invoke(); } }
这段代码与前面Java版的TestMethodHandle1功能基本相同。来观察一下两者的异同点。
要在C#里使用委托的流程是:
0、事先声明好合适的委托类型;
1、适用合适的委托类型,指定目标方法创建出委托的实例;
2、调用委托上的Invoke()方法。
.NET允许用户自定义委托类型,在C#里的语法是:
modifiers delegate return_type DelegateTypeName(argument_list);
该语法与方法的声明语法非常像,只是在返回类型之前多了个delegate关键字而已,比C中typedef函数指针类型的语法容易多了。上例用到的System.Action类型就是标准库里声明好的一个委托类型,其声明形如:
namespace System { public delegate void Action(); }
这样声明出来的委托类型相当于声明了一个Action类,继承System.MulticastDelegate,并且拥有一个返回值类型为void,参数列表为空的Invoke()方法。在C#里,用户无法像声明普通类型一样通过声明一个继承System.Delegate或System.MulticastDelegate的类来得到一个新的委托类型,而只能用上述语法来声明。不过从CLR的角度看,并没有限制用户不能自行继承上述两种类型来声明新的委托类型。
委托是.NET类型系统的一部分。两个委托类型即便表示的签名一致也会被认为是不同的类型,不能相互赋值/转换。这体现出了C#与.NET类型的nominal-typing性质。创建委托实例时则只考虑目标方法与委托类型在签名上是否吻合,而不考虑名字问题,这点又与JDK 7的MethodHandle相似。
委托上的Invoke方法的签名与委托声明的相吻合。在编译时,调用委托的Invoke()方法与调用一般的虚方法一样会被类型检查。
目前在C#里可以显式调用委托上的Invoke()方法,也可以直接把委托当成方法用括号调用。是显式调用Invoke()还是直接用括号调用委托,现在来说只是程序员的偏好问题而已。事实上在C# 2.0以前编译器会阻止程序员显式调用Invoke()方法。
用C#代码例子再稍微解释一下:
// 下面声明两个委托类型,它们的签名是一样的 // (int, int) -> int delegate int BinaryIntOp1(int x, int y); delegate int BinaryIntOp2(int i, int j); // 形式参数的类型是重要的,名字不重要 // 上面两个委托上的Invoke()方法都形如: // [MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)] // public virtual int Invoke(int i, int j); // 注意到Invoke()是一个虚方法,其返回值与参数类型都是确定的, // 而其实现是由运行时直接提供的。 class Demo { // 定义一个签名为(int, int) -> int的方法 public static int Add(int a, int b) { return a + b; } static void Main(string[] args) { // 可以,Add方法与BinaryIntOp1要求的签名匹配 BinaryIntOp1 op1 = new BinaryIntOp1(Add); // 可以,Add方法与BinaryIntOp1要求的签名匹配 // C# 2.0的隐式创建委托的新特性:等同于写成new BinaryIntOp2(Add); BinaryIntOp2 op2 = Add; // 不行,两者属于不同的委托类型,无法相互赋值/转换 //BinaryIntOp2 op3 = op1; // 调用Invoke()方法会被编译器检查类型是否匹配 int sum = op1.Invoke(4, 2); // 下面这样就会在编译时出错: //int sum2 = op2.Invoke(new object(), 0); } }
Java版例子中,创建MethodHandle对象需要在代码里通过MethodHandles.Lookup工厂来查找目标;在C#里编译器已经帮忙找出了目标方法的token,写起来方便许多;C#编译器会根据方法名与委托的签名去寻找合适重载版本的方法,找出它的token,并用于创建委托实例。如果目标方法无法在编译时确定,使用System.Delegate.CreateDelegate(Type, MethodInfo)方法也可以依靠反射信息创建出委托。
CLI的几个主流实现,微软的CLR、Novell的Mono等的JIT编译器都比较静态,遇到虚方法和委托调用都不会内联。从这点说,.NET比JVM的技术复杂度要低一些。不过至少在.NET里使用委托不会带来多少额外开销,所以还是可以放心使用的。
第二组例子,给hello world添加参数:
JDK 7:
import java.dyn.*; import static java.dyn.MethodHandles.*; public class TestMethodHandle2 { private static void hello(String name) { System.out.printf("Hello, %s!\n", name); } public static void main(String[] args) { if (0 == args.length) args = new String[] { "Anonymous" }; MethodType type = MethodType.make(void.class, String.class); MethodHandle method = lookup() .findStatic(TestMethodHandle2.class, "hello", type); method.<void>invoke(args[0]); } }
编译,以下述命令运行
java -XX:+EnableMethodHandles TestMethodHandle2 test
输出结果为:
引用
Hello, test!
基本上跟第一组例子一样,只是让hello()多了个参数而已。留意一下创建MethodType实例的代码如何对应的改变。
第15行对应的Java字节码是:
invokevirtual java/dyn/MethodHandle.invoke:(Ljava/lang/String;)V
留意Java编译器是如何根据调用invoke时传入的参数的静态类型(编译时类型)来决定invoke的方法描述符。(Ljava/lang/String;)V的意思是返回值类型为void,参数列表有一个参数,类型为java.lang.String。
如果把代码稍微修改,使MethodHandle的方法类型与Java编译器推断的调用类型不相符的话:
import java.dyn.*; import static java.dyn.MethodHandles.*; public class TestMethodHandle2 { private static void hello(Object name) { System.out.printf("Hello, %s!\n", name); } public static void main(String[] args) { if (0 == args.length) args = new String[] { "Anonymous" }; MethodType type = MethodType.make(void.class, Object.class); MethodHandle method = lookup() .findStatic(TestMethodHandle2.class, "hello", type); method.<void>invoke(args[0]); } }
编译运行会看到:
引用
Exception in thread "main" java.dyn.WrongMethodTypeException: (Ljava/lang/Object;)V cannot be called as (Ljava/lang/String;)V
at TestMethodHandle2.main(TestMethodHandle2.java:15)
at TestMethodHandle2.main(TestMethodHandle2.java:15)
这演示了Java编译器将invoke的方法类型推断为(Ljava/lang/String;)V,而被调用的MethodHandle实例实际的方法类型却是(Ljava/lang/Object;)V,JVM便认为这个调用不匹配并拒绝执行。关键点是:调用invoke时,参数表达式的静态类型(编译时类型)必须与MethodHandle的方法类型中对于位置的参数类型“准确一致”;虽然String类型的引用可以隐式转换为Object类型的,但不满足“准确一致”的要求。
要想让修改过的TestMethodHandle2再次正确运行,可以把第15行改为:method.<void>invoke((Object)args[0]);,也就是加个类型转换,使Java编译器推断出来的方法描述符为(Ljava/lang/Object;)V。或者也可以加一个适配器:
import java.dyn.*; import static java.dyn.MethodHandles.*; public class TestMethodHandle2 { private static void hello(Object name) { System.out.printf("Hello, %s!\n", name); } public static void main(String[] args) { if (0 == args.length) args = new String[] { "Anonymous" }; MethodType type = MethodType.make(void.class, Object.class); MethodType adaptedType = MethodType.make(void.class, String.class); MethodHandle method = lookup() .findStatic(TestMethodHandle2.class, "hello", type); MethodHandle adaptedMethod = MethodHandles.convertArguments( method, adaptedType); adaptedMethod.<void>invoke(args[0]); } }
这里演示了MethodHandle的可组装性:通过给实际调用目标装一个转换参数类型的适配器,方法调用就又可以成功了。
对比C#的例子:
C# 3.0:
using System; static class TestDelegate2 { static void Hello(string name) { Console.WriteLine("Hello, {0}!", name); } static void Main(string[] args) { if (0 == args.Length) args = new [] { "Anonymous" }; Action<string> method = Hello; method(args[0]); } }
基本上跟第一组例子也是一样的。Action<T>是标准库里预先声明好的一个泛型委托类型,其声明形如:
namespace System { public delegate void Action<T>(T t); }
为了对比,下面也把Hello()的参数类型改为object,
using System; static class TestDelegate2 { static void Hello(object name) { Console.WriteLine("Hello, {0}!", name); } static void Main(string[] args) { if (0 == args.Length) args = new [] { "Anonymous" }; Action<object> method = Hello; method(args[0]); } }
编译和运行都没有任何问题。这里要演示的是Invoke()方法是有确定的签名的,与委托类型声明的相吻合。编译器不会擅自推断Invoke()的签名。
第三组例子,可指定排序条件的快速排序:
前面一直在用hello world作例子或许是无聊了点,下面弄点稍微长一些的。
JDK 7:
import java.dyn.*; import java.util.*; import static java.dyn.MethodHandles.*; import static java.lang.Integer.parseInt; public class TestMethodHandle3 { private static int compareStringsByIntegerValue(String num1, String num2) { return parseInt(num1) - parseInt(num2); } private static int compareStringsByLength(String str1, String str2) { return str1.length() - str2.length(); } public static void sort(String[] array, MethodHandle comparer) { if (0 == array.length) return; sort(array, 0, array.length - 1, comparer); } private static void sort( String[] array, int left, int right, MethodHandle comparer) { if (left >= right) return; String pivot = array[right]; int lo = left - 1; int hi = right; while (true) { while (comparer.<int>invoke(array[++lo], pivot) < 0) { } while (hi > left && comparer.<int>invoke(array[--hi], pivot) > 0) { } if (lo >= hi) break; swap(array, lo, hi); } swap(array, lo, right); sort(array, left, lo - 1, comparer); sort(array, lo + 1, right, comparer); } private static <E> void swap(E[] array, int i, int j) { E temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { String[] array = new String[] { "25", "02", "250", "48", "0024", "42", "2" }; MethodType type = MethodType.make( int.class, String.class, String.class); MethodHandle comparer; comparer = lookup().findStatic( TestMethodHandle3.class, "compareStringsByIntegerValue", type); sort(array, comparer); for (String s : array) System.out.println(s); System.out.println(); comparer = lookup().findStatic( TestMethodHandle3.class, "compareStringsByLength", type); sort(array, comparer); for (String s : array) System.out.println(s); } }
编译,运行,输出结果为:
引用
02
2
0024
25
42
48
250
2
25
42
48
02
250
0024
2
0024
25
42
48
250
2
25
42
48
02
250
0024
核心的sort()方法是个简单的快排实现。为了让用户能够指定排序条件,我让它接收MethodHandle为参数来提供判断逻辑。在没有MethodHandle之前,我可能会选择使用策略模式来达到剥离出部分算法的目的。JDK里的许多API也是这么做的,例如Arrays.sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c),其中的Comparator参数就是“策略对象”。现在就可以直接用一个MethodHandle来代替它了。
例子的整体结构跟前两组没有显著的不同,应该还是比较好理解的。
值得注意的是,我把swap()写为了泛型方法,方便以后再更多地方能复用。排序也应该是个通用算法,为何不把sort()也写为泛型呢?
如果把例子中sort()方法里出现的String全部直接替换为泛型的,变成:
public static <E> void sort(E[] array, MethodHandle comparer) { if (0 == array.length) return; sort(array, 0, array.length - 1, comparer); } private static <E> void sort( E[] array, int left, int right, MethodHandle comparer) { if (left >= right) return; E pivot = array[right]; int lo = left - 1; int hi = right; while (true) { while (comparer.<int>invoke(array[++lo], pivot) < 0) { } while (hi > left && comparer.<int>invoke(array[--hi], pivot) > 0) { } if (lo >= hi) break; swap(array, lo, hi); } swap(array, lo, right); sort(array, left, lo - 1, comparer); sort(array, lo + 1, right, comparer); }
编译没问题,但运行的时候会看到comparer.<int>invoke(...)的调用抛WrongMethodTypeException异常。你可能会纳闷:array数组的元素类型不是E么,是泛型的啊;以String为泛型参数调用sort()的时候,invoke的实际参数类型与comparer的方法类型应该匹配才对,怎么会出错呢?
问题就在于Java的泛型是通过类型擦除法(type-erasure)来实现的,编译过后所有泛型参数都变为了Object,这里也不例外。所以Java编译器推断出来的invoke()的描述符是(Ljava/lang/Object;Ljava/lang/Object;)I,这就出现我们在第二组例子里遇到的问题了——方法类型不准确匹配。
于是解决的办法也很简单,只要改成这样:
public static <E> void sort(E[] array, MethodHandle comparer) { if (0 == array.length) return; Class<Object> objClz = Object.class; MethodType adaptedType = MethodType.make(int.class, objClz, objClz); MethodHandle adaptedComparer = MethodHandles.convertArguments( comparer, adaptedType); sort(array, 0, array.length - 1, adaptedComparer); }
在调用核心的sort()方法前,先加一个适配器来解决类型差异的问题,就万事大吉了 ^ ^
对比C#的例子:
C# 2.0
using System; using System.Collections.Generic; static class TestDelegate3 { static int CompareStringsByIntegerValue(string num1, string num2) { return Convert.ToInt32(num1) - Convert.ToInt32(num2); } static int CompareStringsByLength(string str1, string str2) { return str1.Length - str2.Length; } public static void Sort<T>(T[] array, Comparison<T> comparer) { Sort(array, 0, array.Length - 1, comparer); } static void Sort<T>( T[] array, int left, int right, Comparison<T> comparer) { if (left >= right) return; T pivot = array[right]; int lo = left - 1; int hi = right; while (true) { while (comparer(array[++lo], pivot) < 0) { } while (hi > left && comparer(array[--hi], pivot) > 0) { } if (lo >= hi) break; Swap(ref array[lo], ref array[hi]); } Swap(ref array[lo], ref array[right]); Sort(array, left, lo - 1, comparer); Sort(array, lo + 1, right, comparer); } static void Swap<T>(ref T t1, ref T t2) { T temp = t1; t1 = t2; t2 = temp; } static void Main(string[] args) { String[] array = new String[] { "25", "02", "250", "48", "0024", "42", "2" }; Sort(array, CompareStringsByIntegerValue); foreach (var s in array) Console.WriteLine(s); Console.WriteLine(); Sort(array, CompareStringsByLength); foreach (var s in array) Console.WriteLine(s); } }
基本上也没什么需要特别解释的了。System.Comparison<T>是标准库里预先声明的一个泛型委托类型,其声明形如:
namespace System { public delegate int Comparison<T>(T x, T y); }
注意.NET里委托与泛型可以很好的结合在一起使用,而不必考虑转换参数类型的问题,因为.NET的泛型信息会带到运行时。或者说,“.NET generics are reified”。
如果用上C# 3.0和.NET Framework 3.5的新特性,使用lambda表达式来提供排序依据,
using System; using System.Collections.Generic; static class TestDelegate3 { public static void Sort<T>(T[] array, Func<T, T, int> comparer) { Sort(array, 0, array.Length - 1, comparer); } static void Sort<T>( T[] array, int left, int right, Func<T, T, int> comparer) { if (left >= right) return; T pivot = array[right]; int lo = left - 1; int hi = right; while (true) { while (comparer(array[++lo], pivot) < 0) { } while (hi > left && comparer(array[--hi], pivot) > 0) { } if (lo >= hi) break; Swap(ref array[lo], ref array[hi]); } Swap(ref array[lo], ref array[right]); Sort(array, left, lo - 1, comparer); Sort(array, lo + 1, right, comparer); } static void Swap<T>(ref T t1, ref T t2) { T temp = t1; t1 = t2; t2 = temp; } static void Main(string[] args) { var array = new [] { "25", "02", "250", "48", "0024", "42", "2" }; Sort(array, (s1, s2) => Convert.ToInt32(s1) - Convert.ToInt32(s2)); foreach (var s in array) Console.WriteLine(s); Console.WriteLine(); Sort(array, (s1, s2) => s1.Length - s2.Length); foreach (var s in array) Console.WriteLine(s); } }
我们就不必再为例中用到的排序条件写具名方法了。实际上C#编译器仍然为这两个lambda表达式生成了私有静态方法,我们得到的东西是一样的(硬要说的话,少得到了俩名字),但需要写在代码里的东西减少了。
如果进一步改为使用标准库中现成的方法来排序,
using System; using System.Collections.Generic; static class TestDelegate3 { static void Main(string[] args) { var array = new [] { "25", "02", "250", "48", "0024", "42", "2" }; Array.Sort(array, (s1, s2) => Convert.ToInt32(s1) - Convert.ToInt32(s2)); foreach (var s in array) Console.WriteLine(s); Console.WriteLine(); Array.Sort(array, (s1, s2) => s1.Length - s2.Length); foreach (var s in array) Console.WriteLine(s); } }
整个代码就简洁了许多。
Alright,这帖就介绍MethodHandle与.NET的委托到这里。希望上面的例子达到了简单介绍JDK 7的MethodHandle的目的。
你可能会心存疑惑,“我为什么需要引用别的方法呢?” 由于Java以前一直没有提供这样的功能,一直只使用Java的人可能较少思考这个问题。但你可能会碰到过这样的问题,有时候要调用的目标只有到了运行的时候才知道,无法在代码里直接写方法调用,那怎么办?以前的解决办法就只有通过反射了。一个最简单的使用场景就是,原本通过反射做的方法调用,现在都可以通过MethodHandle完成。如果一个方法频繁被反射调用,开销会很明显,而且难以优化;通过MethodHandle调用则跟正常的接口方法调用速度接近,没有各种包装/装箱的开销,而且可以被JVM优化,何乐而不为?
在MethodHandle出现之前,许多JVM上的脚本语言实现,如JRuby,为了提高调用方法的速度,选择生成大量很小的“invoker类”来对方法签名做特化,通过它们去调用目标方法,避免反射调用的开销。但这么做有许多问题,一是实现麻烦,而是对PermGen堆带来巨大的压力。MethodHandle的出现极大的改善了状况,既便于使用,效率又高,而且还不会对PermGen带来多少压力。Charles Nutter在一帖中描述了这个问题。
(编辑:经指正,Groovy当前还没有用invoke stub,只是做了callsite caching优化。我只读过Groovy的编译器的代码,没读过运行时部分的代码,忽悠了同学们了,抱歉 <(_ _)>)
进一步介绍等以后写invokedynamic的时候再写了~ until then
Have fun ^ ^
评论
12 楼
WenBin_Zhou
2009-09-26
java 的动态代理已经能很好的解决方法调用问题 看不出这里有什么特别的 如果单单只是大大的提高效率 或是它是个轻量级 实在很难说它会很实用
11 楼
RednaxelaFX
2009-09-26
linliangyi2007 写道
如此的Java还是我们心目中的那个纯纯的咖啡嘛!!!
MethodHandle是JSR 292的一部分,而JSR 292主要服务的对象是JVM上的动态语言实现。能为Java带来好处算是个不错的副作用。
Eastsun 写道
这个东东对JVM上的其他语言来说好处大大的
正是如此。Charles Nutter基于invokedynamic实现了个小的动态语言,目前已经比JRuby快了。大家都很期待自己的语言能从invokedynamic挖出多少性能潜力来 ^ ^
10 楼
Eastsun
2009-09-26
这个东东对JVM上的其他语言来说好处大大的
9 楼
iaimstar
2009-09-26
linliangyi2007 写道
如此的Java还是我们心目中的那个纯纯的咖啡嘛!!!
有点同感
8 楼
EQualizer
2009-09-26
编辑掉。。。。。。。。
7 楼
linliangyi2007
2009-09-26
如此的Java还是我们心目中的那个纯纯的咖啡嘛!!!
6 楼
RednaxelaFX
2009-09-26
dennis_zane 写道
JeffreyZhao 写道
C#中的委托非常实用,但Java的这个做法有多少可用性啊
文章中说了,作为重量级的method reflection的替代品还是不错的。比之策略模式来说,这个method handler的调用方式还是比较恶心。
嗯,没错。说真的,由于缺乏在代码中直接表示对方法的引用的语法,要用MethodHandle去替代策略模式挺丑的。在需要“传递一小块代码”的场景中我可能还是会偏向使用匿名内部类,其实也不麻烦。
MethodHandle的主要要解决的问题就是“要高效的调用编译时已知签名但具体目标未知的方法”。可以想像有时候要根据配置去找bean上的getter/setter,或者是要不通过继承而通过聚合去拦截方法,之类的;这种地方以前只能靠反射,如今MethodHandle提供了更轻量且高效的解决方案。
我在这帖里故意没强调MethodHandle跟invokedynamic的关系,因为还有后续的帖在草稿箱里窝着 =v= 我原本是先在记invokedynamic的资料,写着写着发现不先写MethodHandle不行……||||
5 楼
iaimstar
2009-09-26
再次投了关键精华一票~
long~long~long~text
比起可用性,能让invoke的效率提高已经很满足了,指望java把所有事情都做了有点不现实,如果期望更多的灵活性,可以用python和ruby
long~long~long~text
比起可用性,能让invoke的效率提高已经很满足了,指望java把所有事情都做了有点不现实,如果期望更多的灵活性,可以用python和ruby
4 楼
Saito
2009-09-26
JeffreyZhao 写道
C#中的委托非常实用,但Java的这个做法有多少可用性啊
还是蛮实用的吧. 虽然不能代替reflection .但是提高了调用效率 ..
话说reflection 本来在一般系统中用的就不多... method handle会不会在编程范式上有一些新的东西出现呢?
老赵或许可以从c#的委托中预见到将来Java可能会出现的范式. . 跟fx在这里展开谈谈吧.. 洗耳恭听.
3 楼
dennis_zane
2009-09-26
JeffreyZhao 写道
C#中的委托非常实用,但Java的这个做法有多少可用性啊
文章中说了,作为重量级的method reflection的替代品还是不错的。比之策略模式来说,这个method handler的调用方式还是比较恶心。
2 楼
JeffreyZhao
2009-09-26
C#中的委托非常实用,但Java的这个做法有多少可用性啊
1 楼
dennis_zane
2009-09-26
<p>java看起来无可避免地走向复杂化的深渊了,方法引用完全可以不要,只要引入闭包即可,为了曲线救国搞出的这些东西实在是让人犯恶心。看看这样的调用方式,</p>
<pre name="code" class="java"> comparer = lookup().findStatic(
TestMethodHandle3.class,
"compareStringsByIntegerValue",
type);
sort(array, comparer); </pre>
<p><br>实在是悲剧</p>
<p> </p>
<pre name="code" class="java"> comparer = lookup().findStatic(
TestMethodHandle3.class,
"compareStringsByIntegerValue",
type);
sort(array, comparer); </pre>
<p><br>实在是悲剧</p>
<p> </p>
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
Java 8与静态工具类
2014-03-19 08:43 16294以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10465先看看下面这个代码例子, interface IFoo { ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22409(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21886之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48397刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
标题中的“答复: 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比(二)”表明本文将深入探讨Java中的MethodHandle概念,并将其与.NET平台上的委托进行对比。MethodHandle是JDK 7引入的一个强大特性,它...
《深入解析JDK7源代码》 JDK7(Java Development Kit 7)是Java编程语言的一个重要版本,它的源代码对于理解Java平台的工作原理、学习面向对象编程以及提升编程技巧具有极大的价值。在这个资源中,包含了 javax、...
Java Development Kit(JDK)是Java编程语言的核心组件,提供了编写、编译、调试和运行Java应用程序所需的...对比JDK 7与更高版本的源代码,可以帮助开发者追踪语言的发展,理解新特性的实现原理,从而提升编程能力。
标题提到的"jdk-7u191"指的是JDK的第7个主要版本的第191次更新。此版本在发布时是一个重要的里程碑,因为Java 7引入了许多新特性,如动态类型、字符串inswitch、try-with-resources语句等,极大地提高了开发效率。 ...
1. **多语言支持**:Java 7增加了对其他编程语言的支持,如Groovy、Scala等,通过JEP(JDK Enhancement Proposals)292,使得这些语言可以在Java虚拟机(JVM)上运行。 2. **try-with-resources**:这是一个语法糖...
《java jdk 7学习笔记》针对java se 7新功能全面改版,无论是章节架构或范例程序代码,都做了重新编写与全面翻新。并详细介绍了jvm、jre、java se api、jdk与ide之间的对照关系。必要时从java se api的源代码分析,...
10. **模块化系统**:虽然JDK7并未完全实现模块化,但引入了Jigsaw项目的基础,为JDK8的模块化系统奠定了基础。 安装JDK7时,通常需要按照以下步骤进行: 1. 下载并解压"JDK7安装包wins.zip"。 2. 双击安装程序,...
**Java Development Kit (JDK) 7 for Windows 32位详解** JDK(Java Development Kit)是Oracle公司提供的用于开发和运行Java应用程序的核心工具集。JDK 7,也称为Java SE 7 (Java Standard Edition 7),是Java语言...
标签"C#"可能表明此内容与.NET框架的C#编程语言有关,但这里主要讨论的是Java的JDK。尽管C#和Java在很多方面有相似之处,它们是两种不同的编程语言,各有其特性和用途。C#通常与Microsoft的.NET平台关联,而Java则跨...
Java 7(又称为Java SE 7或JDK 1.7)是Java平台标准版的第七个主要版本。它于2011年发布,带来了许多新的特性和改进,包括: - **Strings in Switch语句**:允许在switch语句中直接使用字符串,提高了代码的可读性...
Java Development Kit (JDK) 7 是一个关键的软件开发平台,用于构建和运行Java应用程序。JDK7-API-帮助文档(英文版)是...通过深入阅读和理解这个文档,开发者可以更好地利用Java 7的功能,编写出高效、可靠的代码。
来自OpenJDK网站文件openjdk-7u4-fcs-src-b22-02_may_2012.zip openjdk\jdk\src\share\classes路径 JDK7u4源码2012年4月
通过研究JDK1.1的源代码,开发者可以追溯到Java语言的起源,了解其设计理念和发展脉络,同时也能对比现代Java的改进,从而更好地理解和应用现代编程实践。尽管许多特性已经过时,但它们为后续版本的Java奠定了坚实的...
2. **类型推断**:在Java 7中,编译器可以通过上下文推断变量的类型,这在使用钻石操作符(<> )创建泛型对象时尤为明显,简化了代码。 3. **Strings in switch语句**:扩展了switch语句,使其可以处理String对象,...
虽然描述中没有明确指出Jdk7绿色版包含MySQL,但是提到了“使用”,这可能意味着该版本提供了一种简便的方法来与MySQL配合,可能是通过预配置的连接驱动或其他工具。 关于JDK7的重要知识点: 1. **多线程改进**:...
通过深入研究JDK 7的源码,开发者可以了解到这些特性的实现原理,从而更好地利用它们来编写高效、可靠的Java程序。同时,理解源码也有助于开发者在遇到问题时能够更快地定位和解决问题,提升自身的编程能力。
6. **堆外内存访问**:通过DirectByteBuffer,可以直接在Java代码中访问操作系统分配的堆外内存,提高了处理大块数据的性能。 7. **改进的类型推断**:编译器能够更智能地推断泛型方法的类型参数。 **64位版本的...
7. **国际化与本地化**:`java.text`和`java.util.locale`包处理了文本格式化和本地化需求,源代码揭示了如何根据地区设置调整日期、数字和货币的显示。 8. **网络编程**:`java.net`包包含了网络通信相关的类,如`...
JDK是由Sun Microsystems(现已被Oracle公司收购)开发并维护的,自Java诞生以来,它一直是Java开发者的首选SDK。本文将深入探讨JDK的主要组件、功能及其不同版本的下载链接。 1. JDK的主要组件: - **javac**:...
在类型推断方面,JDK7引入了“钻石操作符”(),使得在创建泛型对象时,编译器能够自动推断类型,减少了代码中的冗余。例如,`List<String> list = new ArrayList();` 这样的写法在JDK7中成为可能。 JDK7还增强了...