该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-09-28
又有看的了...high呀
|
|
返回顶楼 | |
发表时间:2009-09-28
starfeng 写道 引用 定位到一个java方法,其实只需要类型(Class),方法名及参数即可。
... MethodHandle版: private static void test(java.dyn.MethodHandle); Signature: (Ljava/dyn/MethodHandle;)V flags: ACC_PRIVATE, ACC_STATIC LineNumberTable: line 7: 0 line 8: 8 line 7: 15 line 10: 21 Code: stack=4, locals=2, args_size=1 0: iconst_0 1: istore_1 2: iload_1 3: ldc #2 // int 100000 5: if_icmpge 21 8: aload_0 9: iconst_1 10: iconst_2 11: iconst_3 12: invokevirtual #3 // Method java/dyn/MethodHandle.invoke:(III)V ... 普通反射版: private static void test(java.lang.reflect.Method) throws java.lang.Throwable; Signature: (Ljava/lang/reflect/Method;)V flags: ACC_PRIVATE, ACC_STATIC LineNumberTable: line 7: 0 line 8: 8 line 7: 39 line 10: 45 Code: stack=6, locals=2, args_size=1 0: iconst_0 1: istore_1 2: iload_1 3: ldc #2 // int 100000 5: if_icmpge 45 8: aload_0 9: aconst_null 10: iconst_3 11: anewarray #3 // class java/lang/Object 14: dup 15: iconst_0 16: iconst_1 17: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 20: aastore 21: dup 22: iconst_1 23: iconst_2 24: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 27: aastore 28: dup 29: iconst_2 30: iconst_3 31: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 34: aastore 35: invokevirtual #5 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; ... 写得很多,看得也累。。。但感觉整个就是答错了方向. 再加上后面回复的人,有好几个在不懂装懂的,整个就一误人子弟的贴。 MethodHandle我还没来得及深究,但汇编,java字节码还是有不少了解的。 如果你认为性能差距的原因是来自: 引用 17: invokestatic #4 // Method java/lang/Integer.valueOf:
这类代码,那么,麻烦你改一改测试类,统一成 private static void doNothing(Integer x, Integer y, Integer z) { } 再比较字节码。 另外, 引用 35: invokevirtual #5 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
这个代码和 引用 12: invokevirtual #3 // Method java/dyn/MethodHandle.invoke:(III)V
相比,就调用本身来说,是没有差别的,唯一差别是在的两个方法的native c的实现上。如果你分析这段c的代码再得出性能差别的原因,才是找对了方向。 另外,如果我没搞错的话,MethodHandle引入返回值查找方法,其根本原因是来自动态语言的返回值与具体类型无关,和性能本身没什么关系。然后, 引用 如果只是要做Java的method overload resolution,当然只要参数类型不要返回值类型就够了,但了解class文件及JVM内部数据组织方式的话就会知道,方法的签名(signature)在class文件里是以方法描述符(method descriptor)的形式存在,而该描述符上是有返回值类型的。MethodHandles的API这么设计就是为了快,能更直接的访问VM里的信息,以最快的方式找到目标方法。
这个只是你自已的推论吧。这个就如同这么一个sql的比方:原先是查询是id=1, 你说,不够快,id=1&retType=2才更快。 如果MethodHandles.Lookup跟普通反射一样,只通过参数类型去搜索目标方法,那么它与JVM内部所持有的信息就有不匹配的地方,而需要去考虑可能遇到多个匹配结果的问题,然后产生异常发出来,速度自然就慢了。当前的API设计要求指定返回值类型,与JVM持有的信息一致,肯定能找到唯一的方法(或者是方法不存在),速度为何不应该更好? 我之所以选用接收int为参数的类型作为目标方法自然是有目的的,而那就是找一个反射调用是开销非常大的状况,拿来与MethodHandle对比。这样容易显示出差异。事实上一开始我写那两组测试的时候的doNothing就是无参的空方法,后来觉得效果要更明显些比较有趣,就改为接收多个int的。 starfeng 写道 再比较字节码。
另外, 引用 35: invokevirtual #5 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
这个代码和 引用 12: invokevirtual #3 // Method java/dyn/MethodHandle.invoke:(III)V
相比,就调用本身来说,是没有差别的,唯一差别是在内部的native c的实现上。 单就“invokevirtual”指令来说,从字节码上是看不到区别。然而速度的差异来自两部分, 1、Method.invoke在Java代码的一侧有固有的额外开销,包括可变长度参数数组包装以及原始类型参数的自动装箱。即便目标方法接收的参数是引用类型的,没有原始类型参数的装箱开销,数组包装是无法避免的。即使调用method.invoke(null)也会导致一个空Object[]数组的创建。这个并不是单在那条“invokevirtual”上的开销。 starfeng 写道 那么,麻烦你改一改测试类,统一成
//... 这句话请您自己先试了,把结果贴出来,看看数组包装的代码是否仍然存在,而反射的开销是否仍然比MethodHandle大。 2、Method.invoke在VM内的一侧有固有的额外开销,并且会阻碍JIT编译器的优化;而MethodHandle的实现是它比普通反射的固有额外开销少,而且不会阻碍编译器的优化(当前实现尚未完善)。无论是通过MethodHandle.invoke还是通过Method.invoke,我们要达到的目的都是调用“实际目标方法”,而不想去关心这些invoke的实现;也就是说,如果这些invoke本身很复杂,对我们来说就是不利的。同样使用invokevirtual指令去调用“invoke”方法是事实,但跟invoke到实际目标方法的距离没有关系,而这“距离”正是两种方式的性能差异的第二个大头。 你该不会认为“native c”实现的东西的运行代价都小到可以忽略不计吧。另外HotSpot VM是用C++与内联汇编而不是C来实现的,准确说也不是“native c”。 我前面已经提到过Method.invoke会涉及的一些内部实现的方法,用它们做关键字相信你能搜到满意的信息。 简单的说,反射调用的过程中涉及多次安全检查,涉及查询类型信息去找到方法的实际指针,涉及包装类型到原始类型的拆箱,最后还有涉及参数数组解包装,通过stub把参数合适的放到栈上,最后才跑到实际目标方法里;如果其中一些安全检查的结果是可以被cache住的,但另外一些流程则每次调用都要经历,所以速度才慢。 相比之下,MethodHandle.invoke则直接的多。前面例子里用的大多是DirectMethodHandle,它直接指向目标方法,所有安全检查都是在创建MethodHandle的实例是就已经完成,调用MethodHandle.invoke完全不涉及再做安全检查的开销。它不涉及对目标方法的搜索,因为它持有的token信息让VM直接知道目标方法是哪一个。而且由于参数没有被数组包装,它不用经过stub去调整参数而可以直接把参数传递给实际的目标方法。总体来说,在没有被内联的前提下,它跟接口方法调用涉及的一些过程非常相似。现在它速度没有接口方法调用快只是因为JDK 7还在开发过程中,一些既定的实现目标还没达到而已。 嘛……欢迎争论~ =v= 不过请提出确实的依据。如果你调试过,读过相关源码,仍然得到你现在的结论,那我很高兴继续与你争论。 |
|
返回顶楼 | |
发表时间:2009-09-29
看这情况应该引入新的关键字
|
|
返回顶楼 | |