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

欢迎!请参阅关于页面以获取更多有关如何使用本站的信息。

0
编译器

我意识到使用反射调用这类方法从性能上讲不是一个好主意,但这不应该导致不正确或危险的行为。

这里似乎会触发输入 long 类型的静默向下转换,从而得到截断的整数输出

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

我能在 Ubuntu 12.04.2 上的 Java 6 JVM 上重现您所看到的行为

java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.5) (6b27-1.12.5-0ubuntu0.12.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

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

user=> (set! warn-on-reflection 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

在Clojure 1.5.1和以下JVM上观察到上述行为

Ubuntu 12.04.2加上此JVM
java版本 "1.7.0_21"
Java TM SE运行环境(构建 1.7.0_21-b11)
Java HotSpot TM 64位服务器虚拟机(构建 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位服务器虚拟机(构建 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; //Method 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; //Method 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(link: ) args)中,先调用getMethods(),然后调用invokeMatchingMethod()。getMethods()在Java 6和7上返回不同顺序的4个java.lang.Math/abs方法,导致在两个JVM上选择不同的一个。

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

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

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

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

这可能表明invokeMatchingMethod()中存在不可取的行为,它过于依赖于提供给它的方法顺序。

如您所述,类型提示对于避免反射的重大性能影响很有用。

0

由 alexmiller 发布的评论:

在 1.8.0-alpha3 中无法重现。

0

由 bronsa 发布的评论:

Alex,我可以用 1.8.0-master-SNAPSHOT 和 jdk 1.8 进行重现

[~]> java -version

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

0

评论者:jafingerhut

可能与 CLJ-1921 相同,或者至少有一些共同点。

0
参考:https://clojure.atlassian.net/browse/CLJ-1212 (由 alex+import 报告)
...