2024年Clojure状态调查中分享您的想法!

欢迎!请访问关于页面了解更多关于如何工作的信息。

0
编译器

我知道在调用这些方法时依赖反射在性能上并不是一个好主意,但它们不应该导致不正确或危险的行为。

这里它似乎触发了对输入长整型的静默向下转型,给出截断的整数输出

user> (defn f (link: a b) (Math/abs (- a b)))
反射警告,NO_SOURCE_PATH:1:15 - abs调用无法解析。

'user/f

user> (f 1000000000000 2000000000000)
727379968
user> (class (f 1000000000000 2000000000000))
java.lang.Integer
user> (defn f (link: ^long a ^long b) (Math/abs (- a b)))

'user/f

user> (f 1000000000000 2000000000000)
1000000000000
user> (class (f 1000000000000 2000000000000))
java.lang.Long

9 个答案

0

评论由:matthjw

以下是一个复现问题的简单方法

user> (#(Math/abs %) 1000000000000)
反射警告,NO_SOURCE_PATH:1:3 - abs调用无法解析。
727379968

0

评论由:jafingerhut

我在这些Java 6 JVMs上复现了您看到的行为,这些JVMs在Ubuntu 12.04.2上

java版本 "1.6.0_27"
OpenJDK运行环境(IcedTea6 1.12.5)(6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK 64位服务器VM(构建20.0-b12,混合模式)

java版本 "1.6.0_45"
Java SE运行环境(构建1.6.0_45-b06)
Java HotSpot 64位服务器VM(构建20.45-b01,混合模式)

然而,我尝试过两个Java 7 JVM,它给出了以下行为,看起来更接近你所期望的结果。我不知道Java 6和Java 7之间导致这种行为差异的确切区别是什么,但这是一些证据表明这与Java 6与Java 7有关。

用户=> (set! 警告反射 true)
true
用户=> (defn f (link: a b) (Math/abs (- a b)))
反射警告,NO_SOURCE_PATH:1:15 - abs调用无法解析。

'user/f

用户=> (f 1000000000000 2000000000000)
1000000000000
用户=> (class (f 1000000000000 2000000000000))
java.lang.Long

在这些JVM上观察到的上述行为

Ubuntu 12.04.2加上此JVM
java版本"1.7.0_21"
Java TM SE运行时环境(构建1.7.0_21-b11)
Java HotSpot TM 64位服务器VM(构建23.21-b01,混合模式)

Mac OS X 10.8.3加上此JVM
java版本"1.7.0_15"
Java TM SE运行时环境(构建1.7.0_15-b03)
Java HotSpot TM 64位服务器VM(构建23.7-b01,混合模式)

0
_评论者:matthjw_

啊,很有趣。
可能是Java 7中反射API工作方式的不同导致的差异?

以下为字节码生成情况,供有兴趣者参考

public java.lang.Object invoke(java.lang.Object);
  代码
   0:     ldc     #14; //String java.lang.Math
   2:     invokestatic     #20; //方法 java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   5:     ldc     #22; //String abs
   7:     iconst_1
   8:     anewarray     #24; //类 java/lang/Object
   11:     dup
   12:     iconst_0
   13:     aload_1
   14:     aconst_null
   15:     astore_1
   16:     aastore
   17:     invokestatic     #30; //方法 clojure/lang/Reflector.invokeStaticMethod:(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
   20:     areturn
0

评论由:matthjw

只是一个想法(也许这就是Java 7中的情况?),鉴于它是一个静态方法,并且所有可用的重载变体在编译时可能已知,也许它可以生成类似以下代码

(cond
(instance? Long x) (Math/abs (long x)))
(instance? Integer x) (Math/abs (int x)))
;; ...
)

0

评论由:jafingerhut

在Reflector.java的invokeStaticMethod方法中(方法参数为Class c, String methodName, Object(args:raq)),存在对getMethods()的调用,紧跟着是对invokeMatchingMethod()的调用。在Java 6和Java 7中,getMethods()返回了4个不同顺序的java.lang.Math/abs方法,这导致在两个JVM中invokeMatchingMethod()会选取不同的方法。

Java版本 "1.6.0_39"
Java? SE运行时环境(构建版本1.6.0_39-b04)
Java HotSpot哥伦比亚? 64位服务器虚拟机(构建版本20.14-b01,混合模式)

user=> (pprint (seq (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
nil

java版本"1.7.0_21"
Java TM SE运行时环境(构建1.7.0_21-b11)
Java HotSpot TM 64位服务器VM(构建23.21-b01,混合模式)

user=> (pprint (seq (clojure.lang.Reflector/getMethods java.lang.Math 1 "abs" true)))
(#
#
#
#)
nil

这可能是invokeMatchingMethod()中的不良行为迹象,该方法过于依赖于提交给它的方法的顺序。

正如您提到的,类型提示有助于避免反射导致的性能显著下降。

0

评论由:alexmiller发表

在1.8.0-alpha3版本上无法重现。

0

评论由:bronsa发表

亚历克斯,我可以用1.8.0-master-SNAPSHOT和jdk 1.8重放。

[~]> java -version java version "1.8.0_45" Java? SE运行时环境(构建版本1.8.0_45-b14)Java HotSpot? 64位服务器虚拟机(构建版本25.45-b02,混合模式)[~]> clj Clojure 1.8.0-master-SNAPSHOT user=> (set! *warn-on-reflection* true) true user=> (#(Math/abs %) 1000000000000) 反射警告,NO_SOURCE_PATH:2:3 -无法解析调用静态方法abs(参数727379968 user=> (class *1) java.lang.Integer)

安迪的最后一条评论提到clojure.lang.Reflector.invokeStaticMethod依赖于传递给它的方法的顺序,而且这个顺序可以在不同的jdk版本之间改变,所以可能这就是你无法重现它的原因。

0

评论由:jafingerhut

可能与CLJ-1921相同,或者是其中的一些共同点。

0
参考:https://clojure.atlassian.net/browse/CLJ-1212 (由alexisport提交)
...